C++ RAII 与智能指针
什么是RAII
资源获取即初始化(Resource Acquisition Is Initialization, RAII),RAII 是一种资源管理机制,资源的有效期与持有资源的对象生命周期严格绑定,即由对象的构造函数完成资源分配,由析构函数完成资源释放。总结一句话就是:用对象来管理资源。
RAII实现原理
当一个对象离开作用域的时候就会被释放,会调用这个对象类的析构函数,这都是自动管理的,不需要我们手动调用。所以我们可以把资源封装到类的内部,当需要用资源的时候初始化对象即可,当对象被释放的时候资源也会被释放。
看一个小例子:
1 | #include <iostream> |
输出结果如下:
可以看到在 test 函数结束之后会释放对象 arrayOp,然后会调用 ArrayOperation 的析构函数,释放掉指针。
简单说明一下,创建 arrayOp:
1 | ArrayOperation arrayOp; |
是在栈中分配内存,如果:
1 | ArrayOperation* arrayOp = new ArrayOperation(); |
则是在堆中分配内存,如果不:
1 | delete arrayOp; |
则不会调用析构函数。
智能指针
auto_ptr
auto_ptr 的特性就是在栈上构建对象,在离开作用范围时会自动析构。
上面的例子可以改成:
1 | void test(){ |
这样的话不需要 delete arrayOp,也会调用析构函数来释放资源。
auto_ptr 有一个问题,就是被销毁时会自动删除它指向的对象,所以不能让多个 auto_ptr 指向同一个对象。如果有多个 auto_ptr 指针指向同一个对象,便会多次删除对象。
但是若通过 copy 构造函数或者 copy assignment 操作符复制它们,原指针就会变成 null,而复制所得的指针将取得资源的唯一控制权(所有权)。注意,是拷贝初始化或者拷贝赋值才会这样,即发生了拷贝行为,原指针会变为 null。
1 | int*p=new int(0); |
这样 ap1、ap2 都指向 p 所指之物,危险行为,会删除 p 两次。
如果
1 | int*p=new int(0); |
ap2 是拷贝初始化,ap1 会变为 null,ap2 指向 p 所指之物。
而且因为 auto_ptr 的析构函数中删除指针用的是 delete,而不是 delete[],所以我们不应该用 auto_ptr 来管理一个数组指针!!
shared_ptr
shared_ptr 也是一种智能指针,自动管理对象的释放,并且可以让多个指针指向同一个对象,会有一个类似 iOS ARC 中“引用计数”的东西来记录这个对象被多少指针所引用。如果这个对象的引用计数变为 0,则会自动释放空间。
shared_ptr 跟 auto_ptr 一样自动管理对象的释放,并且没有 auto_ptr 的那些缺点,因为使用了“引用计数”。但是这样也产生了新的问题,那就是循环引用、自引用等问题。
weak_ptr
由于 shared_ptr 有了循环引用等问题,于是就有了 weak_ptr 这种弱引用的指针,不增加引用计数。
这里和 iOS Property 中的机制差不多,很多地方的实现思想都是相似的。
unique_ptr
unique_ptr 是只能有一个 unique_ptr 指向一个对象,因此 unique_ptr 不支持拷贝和赋值。
1 | unique_ptr<string> p1(new string("test")); |
不能拷贝或者赋值,但是可以通过 release 或 reset 来转换指针的所有权。
1 | unique_ptr<string> p2(p1.release); //release将p1置空 |
参考
C++中的RAII机制
Effective C++ 第13条款
C++ Primer