十、 C++特性之智能指针
Smart Pointers 智能指针
已经有成千上万的文章讨论这个问题了,所以我只想说:现在能使用的,带引用计数,并且能自动释放内存的智能指针包括以下几种:
- unique_ptr: 如果内存资源的所有权不需要共享,就应当使用这个(它没有拷贝构造函数),但是它可以转让给另一个unique_ptr(存在move构造函数)。
- shared_ptr: 如果内存资源需要共享,那么使用这个(所以叫这个名字)。
- weak_ptr: 持有被shared_ptr所管理对象的引用,但是不会改变引用计数值。它被用来打破依赖循环(想象在一个tree结构中,父节点通过一个共享所有权的引 用(chared_ptr)引用子节点,同时子节点又必须持有父节点的引用。如果这第二个引用也共享所有权,就会导致一个循环,最终两个节点内存都无法释 放)。
另一方面,auto_ptr已经被废弃,不会再使用了。
什么时候使用unique_ptr,什么时候使用shared_ptr取决于对所有权的需求,我建议阅读以下的讨 论:http://stackoverflow.com/questions/15648844/using-smart-pointers-for- class-members
以下第一个例子使用了unique_ptr。如果你想把对象所有权转移给另一个unique_ptr,需要使用std::move(我会在最后几段讨论这个函数)。在所有权转移后,交出所有权的智能指针将为空,get()函数将返回nullptr。
1 void foo(int* p) 2 { 3 std::cout << *p << std::endl; 4 } 5 std::unique_ptr<int> p1(new int(42)); 6 std::unique_ptr<int> p2 = std::move(p1); // transfer ownership 7 8 if(p1) 9 foo(p1.get()); 10 11 (*p2)++; 12 13 if(p2) 14 foo(p2.get());
第二个例子展示了shared_ptr。用法相似,但语义不同,此时所有权是共享的。
1 void foo(int* p) 2 { 3 } 4 void bar(std::shared_ptr<int> p) 5 { 6 ++(*p); 7 } 8 std::shared_ptr<int> p1(new int(42)); 9 std::shared_ptr<int> p2 = p1; 10 11 bar(p1); 12 foo(p2.get());
第一个声明和以下这行是等价的:
1 auto p3 = std::make_shared<int>(42);
make_shared<T>是一个非成员函数,使用它的好处是可以一次性分配共享对象和智能指针自身的内存。而显示地使用 shared_ptr构造函数来构造则至少需要两次内存分配。除了会产生额外的开销,还可能会导致内存泄漏。在下面这个例子中,如果seed()抛出一个 错误就会产生内存泄漏。
1 void foo(std::shared_ptr<int> p, int init) 2 { 3 *p = init; 4 } 5 foo(std::shared_ptr<int>(new int(42)), seed());
如果使用make_shared就不会有这个问题了。第三个例子展示了weak_ptr。注意,你必须调用lock()来获得被引用对象的shared_ptr,通过它才能访问这个对象。
1 auto p = std::make_shared<int>(42); 2 std::weak_ptr<int> wp = p; 3 4 { 5 auto sp = wp.lock(); 6 std::cout << *sp << std::endl; 7 } 8 9 p.reset(); 10 11 if(wp.expired()) 12 std::cout << "expired" << std::endl;
如果你试图锁定(lock)一个过期(指被弱引用对象已经被释放)的weak_ptr,那你将获得一个空的shared_ptr.