C++中的智能指针
C++中有很多智能指针如 unique_ptr 和 shared_ptr ,本篇文章主要介绍这些指针的功能和应用场景。
unique_ptr
unique_ptr 用于保证作用域范围内的指针能够被释放,有效避免一些情况下跳出作用域而没有对动态内存进行释放引起的内存泄漏。
unique_ptr 对象支持移动(move),不支持拷贝(copy)。 unique_ptr 在移动后(move)第一个会失效(变为空指针),这是move的特性。
unique_ptr 作为函数参数进行传递时需要使用move完成,示例代码如下。
// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p) {
p->bar();
return p;
}
auto p = std::make_unique<D>(); // p is a unique_ptr that owns a D
auto q = pass_through(std::move(p));
assert(!p); // now p owns nothing and holds a null pointer
q->bar(); // and q owns the D object
unique_ptr 所管理对象被销毁的时机:
The object is disposed of using the associated deleter when either of the following happens:
- the managing
unique_ptrobject is destroyed
- the managing
unique_ptrobject is assigned another pointer via operator= or reset().
shared_ptr 与 auto_ptr
auto_ptr 是 shared_ptr 的前身, 在C++11中开始支持 shared_ptr ,并弃用了 auto_ptr ,所以在新开发的项目中应该都使用 shared_ptr 。
shared_ptr 所管理对象被销毁的时机:
The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining
shared_ptrowning the object is destroyed;
- the last remaining
shared_ptrowning the object is assigned another pointer viaoperator=orreset().
shared_ptr 在何时会增加引用计数( use_count )呢?
- 使用等号进行对象的拷贝
- 作为函数的参数进行传递
推荐使用 make_shared 创建 shared_ptr 对象,因为这样比使用指针进行创建效率更高。
不要用一个动态分配内存的指针同时创建两个 shared_ptr 对象,也不要再手动对这个动态指针进行释放,如下所示的代码是错误的。
// !! 这是错误代码的示例
int* pInt = new int(10);
std::shared_ptr<int> pShared1(pInt);
std::shared_ptr<int> pShared2(pInt); // error
delete pInt; // error
在使用 shared_ptr 存放动态申请的数组时,要手动指定 deleter ,也就是对象销毁函数,参考如下示例代码。
shared_ptr<int> pInt(new int[3], [](int* p){delete[] p;});
weak_ptr
weak_ptr 是从c++11开始支持的,用于存放已经由 shared_ptr 管理的对象。在 weak_ptr 需要操作其管理的对象时,需要先转化为 shared_ptr 再进行操作。
weak_ptr 保证的是临时的所有权:只有在对象存在时才可以访问该对象,由于该对象可能会被释放掉, weak_ptr 用于追踪(track)该对象,在需要操作该对象时会转换为一个临时的 shared_ptr 以获得临时的所有权。如果这时原始的 shared_ptr 被销毁,则该对象的生存期会延长至这个临时的 shared_ptr 也被销毁。
weak_ptr 的另一个用法是打破由 shared_ptr 组成的引用环(reference cycle)。用互锁来形容比较贴切,在这种情况下引用计数永远不会清零,因此会产生内存泄漏。需要将引用环中的一个 shared_ptr 修改为 weak_ptr 以解决这个问题。
以下是示例代码
class A { std::shared_ptr<B> b; ... };
class B { std::shared_ptr<A> a; ... };
std::shared_ptr<A> x(new A); // +1
x->b = std::make_shared<B>(); // +1
x->b->a = x; // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)