C++11 智能指针

C++11中一共引入了三种智能指针。

std::unique_ptr

管理独占资源的指针,在任何时间点,资源只能唯一地被一个unique_ptr占有。当unique_ptr离开作用域,所包含的资源被释放。

std::unique_ptr<int> uptr(new int);
std::unique_ptr<int[]> uptra(new int[5]);

std::unique_ptr不提供复制语义(不允许拷贝构造和拷贝赋值),只支持移动语义(通过std::move移动)。当把std::unique_ptr赋给另外一个对象时,资源的所有权就会被转移。

std::unique_ptr<int> uptr(new int);
std::unique_ptr<int> uptr2 = uptr; // 错误,不允许左值赋值
std::unique_ptr<int> uptr2 = std::move(uptr); // 正确,赋值后uptr指针不指向任何对象

std::shared_ptr

管理共享的指针,允许多个拷贝。内部使用了引用计数的机制来保证其安全性,当最后一个shtard_ptr离开作用域时,内存才会自动释放。

std::shared_ptr<int> sptr(new int(10));
std::shared_ptr<int> sptr2 = std::make_shared<int> (10);
std::shared_ptr<int> sptr3 = sptr2; // 引用计数增加

上面的代码,当sptr2赋值给sptr3后,两者都指向同一个对象,该对象的引用计数变为2。这样的计数被称为强引用(strong reference),此外,还有一种引用计数叫做弱引用(weak reference)。我们接下来会介绍。

std::weak_ptr

为什么会有weak_ptr这个类型呢,我们来看下面的这个shared_ptr循环引用的例子。

class B;
class A
{
public:
A() : m_sptrB(nullptr) { };
~A()
{
cout<<" A is destroyed"<<endl;
}
std::shared_ptr<B> m_sptrB;
};
class B
{
public:
B( ) : m_sptrA(nullptr) { };
~B( )
{
cout<<" B is destroyed"<<endl;
}
std::shared_ptr<A> m_sptrA;
};

int main( )
{

std::shared_ptr<B> sptrB( new B );
std::shared_ptr<A> sptrA( new A );
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
}

上面的例子中,对象A引用对象B,对象B又引用对象A。这样的结果会导致离开main函数后sptrAsptrB的引用计数无法降为0,造成内存泄漏。

为了解决这个问题,C++11引入了weak_ptr,其拥有和share_ptr类似的共享语义,但是并不真正持有资源。share_ptr中的弱引用计数指的就是weak_ptr的引用次数。当强引用计数变为0时,share_ptr所持有的的资源就会被释放(即使当前弱引用计数不为0)。

weak_ptr并不真正持有资源,因此也不支持普通指针对应的*,->操作符。当需要使用weak_ptr指向的资源时,我们需要把weak_ptr转换为shared_ptr,可以使用lock()方法来完成对应的操作。

std::shared_ptr<int> sptr(new int);
std::weak_ptr<int> wptr = sptr;

std::shared_ptr<int> wsptr = wptr.lock();

我们可以使用weak_ptr来解决上面的循环引用问题。

class B;
class A
{
public:
A() : m_sptrB(nullptr) { };
~A()
{
cout<<" A is destroyed"<<endl;
}
std::weak_ptr<B> m_sptrB;
};
class B
{
public:
B( ) : m_sptrA(nullptr) { };
~B( )
{
cout<<" B is destroyed"<<endl;
}
std::weak_ptr<A> m_sptrA;
};

int main( )
{

std::shared_ptr<B> sptrB( new B );
std::shared_ptr<A> sptrA( new A );
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
}

这里我们根据语义将类AB对对方的引用替换为若引用,可以解决问题。实际上,只需要将A或者B中任意一个的指针替换为weak_ptr就可以解决循环引用的问题。

参考资料

https://www.jianshu.com/p/e4919f1c3a28

此文有用? 求鼓励!

显示 Gitment 评论