C++11新特性之智能指针
1、shared_ptr:一种计数指针,被指向的对象在引用计数为0时删除。它表示共享的所有权(负责对象的删除销毁)。需要包含<memory>,下同。
// 定义删除器 struct Deleter { public: void operator() (Base *p) { cout << "[deleter called]" << endl; delete p; } }; int main() { shared_ptr<Base> p1(new Base, Deleter()); { shared_ptr<Base> p2(p1); // 这里p1/p2的use_count()为2 { shared_ptr<Base> p3(p2); // 这里p1/p2/p3的use_count()为3 } // p1/p2的use_count()变为2 } // p1的use_count()变为1 p1.reset(); // 无参的reset()将使p1变为空(就像默认构造时一样) // p1的use_count()变为0且上面的new Base被删除/析构 return 0; }
以下用法是错误的:
// 错误1:既使用shared_ptr,又自己管理原生指针。原生指针被重复析构 int *p = new int(9); shared_ptr<int> sp1(p); delete p; // 错误2:多个shared_ptr均从同一个原生指针构造。原生指针被重复析构 // 多个shared_ptr管理同一个原生指针,则后续的shared_ptr都应直接或间接(通过weak_ptr)从第一个构造 int *q = new int(10); shared_ptr<int> sp2(q); shared_ptr<int> sp3(q);
有时候,需要在类的成员函数里面获取到“指向”当前对象的shared_ptr(然后把它传递给其他函数)。此时,只需要继承enable_shared_from_this,即可在成员函数中使用shared_from_this()获取到shared_ptr。示例:
class Foo; class Bar { public: void test(shared_ptr<Foo> sp); }; class Foo : public enable_shared_from_this<Foo> { public: void test(); void print(); }; void Bar::test(shared_ptr<Foo> sp) { // 此时sp.use_count()为3 sp -> print(); } void Foo::test() { // 若换成shared_ptr<Foo> sp = shared_ptr<Foo>(this);会出错 // 这相当于使用一个原生指针分别构造两个shared_ptr,会造成重复释放的问题 shared_ptr<Foo> sp = shared_from_this(); // 此时sp.use_count()为2 Bar bar; bar.test(sp); // 此时sp.use_count()为2 } void Foo::print() { cout << "in Foo::print" << endl; } int main() { shared_ptr<Foo> sp1(new Foo); sp1 -> test(); // 此时sp1.use_count()为1 // !!!错误用法,抛出bad_weak_ptr异常!!! // 原因(参考boost的enable_shared_from_this.hpp):enable_shared_from_this有一个weak_ptr类型的成员, // 它只能由shared_ptr通过enable_shared_from_this的一个成员函数赋值(故使用以下方式时,该成员为空), // shared_from_this()正是通过该成员“获取”到shared_ptr Foo *fp = new Foo; fp -> test(); delete fp; }
shared_ptr对象可以在共享一个指针P(owned pointer)所有权(负责其释放)的同时保存另一个指针P'(stored pointer,不负责其释放)。通常P和P'是相同的,但通过别名构造器(aliasing constructor)可使shared_ptr拥有不同的P和P':
struct C { C() : data(new int(10)) {} ~C() {delete data; data = NULL;} int *data; }; int main () { std::shared_ptr<C> obj(new C); // sp1和sp2的use_count均为3 // shared_ptr的*运算符和get()函数操作的均是其保存的指针P',如*sp1==10
// 另外,注意sp1和sp2的类型是其保存的指针P'的类型! std::shared_ptr<int> sp1(obj, obj -> data); std::shared_ptr<int> sp2(sp1);
return 0; }
关于类型转换:
template <class T, class U> shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp) noexcept;
static_pointer_cast即shared_ptr的static_cast:(sp非空时)返回的对象与sp共享owned pointer,且将sp的stored pointer转换为T*类型(这里需要确保static_cast<T*>(sp.get())是合法的)。如下例:
class Test {}; class Base {}; class Derived : public Base {}; int main() { std::shared_ptr<Test> tsp(new Test); std::shared_ptr<Base> bsp0(new Base); std::shared_ptr<Base> bsp(tsp, bsp0.get()); std::shared_ptr<Derived> dsp = std::static_pointer_cast<Derived>(bsp); // 现在tsp、bsp0、bsp和dsp的use_count分别为3、1、3、3 return 0; }
关于排序:重载的<运算符考虑的是stored pointer的地址;而owner_before()考虑的则是owned pointer的地址,它的排序结果在shared_ptr被用于关联容器(如map)的key时使用,两个shared_ptr对象仅在共享同一指针或均为空时相等。
2、weak_ptr:Weak shared pointer
int main() { shared_ptr<int> sp1(new int(10)); // 可以用shared_ptr/weak_ptr构造weak_ptr // weak_ptr拥有资源的访问权,但没有所有权(不会增加引用计数) weak_ptr<int> wp(sp1); // wp和sp1的use_count()均为1 if (!wp.expired()) { // lock():从weak_ptr“获取”一个可用的shared_ptr对象 shared_ptr<int> sp2 = wp.lock(); // wp和sp1的use_count()均为2 *sp2 = 100; } // 此时wp和sp1的use_count()均为1 return 0; }
expired():检查weak_ptr对象是否失效(为空或已经没有shared_ptr和它一起观测某个资源)。和空weak_ptr一样,失效的weak_ptr不能用于还原出shared_ptr(lock())。expired()和use_count()==0返回相同的结果,但效率更高。示例:
weak_ptr<int> wp; // 此时wp.expired()为1 shared_ptr<int> sp1(new int); wp = sp1; shared_ptr<int> sp2 = wp.lock(); // 此时wp.expired()为0 sp2.reset(); // 此时wp.expired()为0 sp1.reset(); // 此时wp.expired()为1
另外,weak_ptr没有重载*和->运算符,不能像普通指针一样使用(要借助lock(),参考第一个例子中的sp2)。
shared_ptr还有“环状引用”的问题,也需要使用weak_ptr来规避,如下例:
class parent; class children; typedef shared_ptr<parent> parent_ptr; // 改为typedef weak_ptr<parent> parent_ptr;方可避免“环状引用”的问题 typedef shared_ptr<children> children_ptr; // 改为typedef weak_ptr<children> children_ptr;方可避免“环状引用”的问题 class parent { public: children_ptr children; }; class children { public: parent_ptr parent; }; int main() { shared_ptr<parent> father(new parent); shared_ptr<children> son(new children); father -> children = son; son -> parent = father; // father和son的use_count()均为2,程序退出时没有调用parent和children的析构函数!!! return 0; }
3、unique_ptr:
1)在了解unique_ptr之前,先来回顾一下auto_ptr。
auto_ptr:Automatic Pointer(自C++11起已弃用)。
auto_ptr对象对它管理的指针拥有所有权,即负责其内存释放(释放时机:auto_ptr对象销毁时)。
和shared_ptr一样,用同一个原生指针初始化多个auto_ptr对象会导致重复释放内存的错误。
class Base { public: Base() {cout << "in Base" << endl;} ~Base() {cout << "in ~Base" << endl;} void test() {cout << "in test" << endl;} }; int main() { auto_ptr<int> ap1(new int); // 基本操作 *ap1 = 10; int *p = ap1.get(); // *p == 10 auto_ptr<Base> ap2(new Base); ap2 -> test(); auto_ptr<Base> ap3 = ap2; // 赋值或“复制”构造时会转移所有权(故不能用于STL容器中):ap2.get()变成NULL ap3 -> test(); auto_ptr<Base> ap4(new Base); Base *bp = ap4.release(); // ap4.get()变成NULL。释放所有权,不再对之前管理的指针(的内存)负责。返回之前管理的指针 delete bp; auto_ptr<Base> ap5(new Base); ap5.reset(); // “显式”销毁/析构其指向的对象。可设置指向新的对象 return 0; }
2)unique_ptr:管理指针(负责其内存释放),提供有限的垃圾回收机制,相比于原生指针几乎没有额外开销(取决于使用的deleter)。
(1)只支持move赋值/构造,不支持拷贝赋值/构造(因为它们被定义为deleted function):
int main () { std::unique_ptr<int> foo(new int (101)); std::unique_ptr<int> bar; // bar = foo; // 报错:use of deleted function bar = std::move(foo); // 此时foo == nullptr,*bar == 101 return 0; }
// 拷贝构造、拷贝赋值均不被允许 unique_ptr (const unique_ptr&) = delete; unique_ptr& operator= (const unique_ptr&) = delete;
参考资料:
http://www.cplusplus.com
不断学习中。。。