c++ 智能指针 解析
智能指针
简介
有关内存的分配与释放。我们往往会遇到以下的问题。
- 由于疏忽忘记释放 new 出来的堆空间。
- 由于代码逻辑的原因,在 if 判断, try-catch 异常中直接退出函数块,导致并没有执行函数末尾的 delete 逻辑。
智能指针的解决方案 :分配的动态内存都交由有生命周期的对象来处理,那么在对象过期时,让它的析构函数删除指向的内存。
c++11 提供了 unique_ptr、shared_ptr 和 weak_ptr 方便我们去管理内存。
auto_ptr
在这里先说一下 c++98 提供的 auto_ptr :
- 与 unique_ptr 类似,都是对资源的独占性。
- 但是在 C++11 后 auto_ptr 已经被“抛弃”,具体的原因有以下几点 :
- 复制或者赋值都会改变资源的所有权
1
2
3
4
5
6
7
8
9
10auto_ptr<string> p1(new string("Hello"));
auto_ptr<string> p2(new string("World"));
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;
p1 = p2; // p2赋值给p1,p1先释放自身管理的资源,然后接收p2所管理的指针,
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl; // p2 : 00000000 已经被释放 - STL容器要求元素必须支持可复制和可赋值
1
2
3
4
5
6
7
8
9
10vector<auto_ptr<string>> vec;
auto_ptr<string> p3(new string("Tiny"));
auto_ptr<string> p4(new string("Sky"));
// 必须使用std::move修饰成右值,才可以进行插入容器中
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));
vec[0] = vec[1]; // 如果进行赋值,vec[1]的资源会被释放
cout << "vec[1]:" << *vec[1] << endl; // 此时访问 vec[1] 会访问越界 - 不支持对象数组的内存管理
1
auto_ptr<int[]> array(new int[10]); // 定义出错
- 复制或者赋值都会改变资源的所有权
unique_ptr
unique_ptr 相较于 shared_ptr 是对资源的独占。
- 由于 unique_ptr 的独占性,无法进行左值的复制构造与赋值,但是运行右值的复制构造与赋值。
1 | unique_ptr<string> p1(new string("Tiny")); |
在 STL 容器中使用unique_ptr,不允许直接赋值,同 auto_ptr 一致,资源被释放,导致访问越界。
支持对象数组的内存管理
1
unique_ptr<int[]> array(new int[10]);
reset()
智能指针的 reset 方法用于重置一个新的管理对象,原先的管理对象会被释放。
1 | auto_ptr<string> p1; |
shared_ptr
shared_ptr 相对于 unique_ptr 则是对资源的共享,通过引用计数来管理,如果计数为0则释放该内存。
构造
1
2
3
4
5
6
7
8
9
10
11
12
13shared_ptr<User> sp1;
Person *user = new User();
sp1.reset(user); // 托管 user ,引用计数为1.
shared_ptr<User> sp2(new user());
shared_ptr<User> sp3(sp1); // sp2的资源交给sp3,sp2的引用计数-1,sp3的计数+1。
shared_ptr<User[]> sp4; // C++17 后支持可以指向类型为T[]的数组对象。
shared_ptr<User[]> sp5(new User[5] { 3, 4, 5, 6, 7 });
// 使用 make_shared 初始化对象
shared_ptr<string> sp6 = make_shared<string>("Tiny-Sky");
shared_ptr<User> sp7 = make_shared<User>(9);陷阱
shared_ptr作为被管控的对象的成员时,会因循环引用造成无法释放资源!
1 | class A { |
weak_ptr
为解决在使用shared_ptr智能指针时,造成的循环依赖问题,可以引用一种弱引用 weak_ptr 来解决。
weak_ptr 更多的是为配合 shared_ptr 而进行管理工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少
weak_ptr 没有重载 * 和 -> 但可以使用 lock 获得一个可用的 shared_ptr 对象。
1 | shared_ptr<A> ptrA(new A()); |
总结
在 c++ 中但凡是需要用到指针的地方都想用智能指针而抛弃了传统的裸指针,是很愚蠢的。
对于智能指针的使用要基于对管理对象的考量,而这种考量更多的从所有权与生命周期方面出发。
- 将由智能指针管理的数据的所有权交给另一个对象,该对象释放,则指针指针管理的数据释放。
- 将由指针指针管理的数据的生命周期管理到一个代码块,该代码块结束,则指针指针管理的数据释放。
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自tiny-sky 的个人博客