C++11智能指针之shared_ptr

shared_ptr

第一种智能指针是shared_ptr,它有一个叫做共享所有权(sharedownership)的概念。shared_ptr的目标非常简单:多个指针可以同时指向一个对象,当最后一个shared_ptr离开作用域时,内存才会自动释放。

创建:

void main( )
{
 shared_ptr<int> sptr1( new int );
}

使用make_shared宏来加速创建的过程。因为shared_ptr主动分配内存并且保存引用计数(reference count),make_shared 以一种更有效率的方法来实现创建工作。

void main( )
{
 shared_ptr<int> sptr1 = make_shared<int>(100);
}

上面的代码创建了一个shared_ptr,指向一块内存,该内存包含一个整数100,以及引用计数1.如果通过sptr1再创建一个shared_ptr,引用计数就会变成2. 该计数被称为强引用(strong reference),除此之外,shared_ptr还有另外一种引用计数叫做弱引用(weak reference),后面将介绍。

shared_ptr<int> sptr2 = sptr1; //引用计数增加为2

通过调用use_count()可以得到引用计数, 据此你能找到shared_ptr的数量。当debug的时候,可以通过观察shared_ptrstrong_ref的值得到引用计数。

在这里插入图片描述

析构

shared_ptr默认调用delete释放关联的资源。如果用户采用一个不一样的析构策略时,他可以自由指定构造这个shared_ptr的策略。下面的例子是一个由于采用默认析构策略导致的问题:

class Test
{
public:
 Test(int a = 0 ) : m_a(a)
 {
 }
 ~Test( )
 {
  cout<<"Calling destructor"<<endl;
 }
public:
         int m_a;
};
void main( )
{
 shared_ptr<Test> sptr1( new Test[5] );
}

在此场景下,shared_ptr指向一组对象,但是当离开作用域时,默认的析构函数调用delete释放资源。实际上,我们应该调用delete[]来销毁这个数组。用户可以通过调用一个函数,例如一个lamda表达式,来指定一个通用的释放步骤。

void main( )
{
 shared_ptr<Test> sptr1( new Test[5], 
        [ ](Test* p) { delete[ ] p; } );  
    /*
    //lamda表达式等价于:调用函数
    void del(Test* p){
        delete[ ] p;
    }
    */
}

接口

就像一个普通指针一样,shared_ptr也提供解引用操作符*,->。除此之外,它还提供了一些更重要的接口:

get(): 获取shared_ptr绑定的资源.

reset(): 释放关联内存块的所有权,如果是最后一个指向该资源的shared_ptr,就释放这块内存。

unique: 判断是否是唯一指向当前内存的shared_ptr.

operator bool : 判断当前的shared_ptr是否指向一个内存块,可以用if 表达式判断。

OK,上面是所有关于shared_ptr的描述,但是shared_ptr也有一些问题:

问题1:

void main( )
{
 shared_ptr<int> sptr1( new int );
 shared_ptr<int> sptr2 = sptr1;
 shared_ptr<int> sptr3;
 sptr3 =sptr1

下表是上面代码中引用计数变化情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEM1pJxN-1592035637452)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592030309650.png)]

所有的shared_ptrs拥有相同的引用计数,属于相同的组。上述代码工作良好,让我们看另外一组例子。

void main( )
{
 int* p = new int;
 shared_ptr<int> sptr1( p);
 shared_ptr<int> sptr2( p );
}

上述代码会产生一个错误,因为两个来自不同组的shared_ptr指向同一个资源。下表给你关于错误原因的图景:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oymp4roH-1592035637456)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592030288764.png)]

避免这个问题,尽量不要从一个裸指针(naked pointer)创建shared_ptr.

问题2

class B;
class A
{
public:
 A(  ) : m_sptrB(nullptr) { };
 ~A( )
 {
  cout<<" A is destroyed"<<endl;
 }
 shared_ptr<B> m_sptrB;
};
class B
{
public:
 B(  ) : m_sptrA(nullptr) { };
 ~B( )
 {
  cout<<" B is destroyed"<<endl;
 }
 shared_ptr<A> m_sptrA;
};
//***********************************************************
void main( )
{
 shared_ptr<B> sptrB( new B );
 shared_ptr<A> sptrA( new A );
 sptrB->m_sptrA = sptrA;
 sptrA->m_sptrB = sptrB;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EvBuZn2l-1592035637462)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592030477049.png)]、

分析:什么都没有输出!

上面的代码产生了一个循环引用.AB有一个shared_ptr, BA也有一个shared_ptr ,与sptrAsptrB关联的资源都没有被释放,参考下表:

在这里插入图片描述
sptrAsptrB离开作用域时,它们的引用计数都只减少到1,所以它们指向的资源并没有释放!!!!!

总结:

1.如果几个shared_ptrs指向的内存块属于不同组,将产生错误。(问题1)

2.如果从一个普通指针创建一个shared_ptr还会引发另外一个问题。在上面的代码中,考虑到只有一个shared_ptr是由p创建的,代码可以好好工作。万一程序员在智能指针作用域结束之前删除了普通指针p。天啦噜!!!又是一个crash。

int main()
{
	int* p = new int;
	shared_ptr<int> sptr1(p);
	shared_ptr<int> sptr2(p);

	delete p; //oops!

	*p = 50;
	cout << *p << endl;

	getchar();
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7ANkPaL-1592035637467)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592035524174.png)]

3.循环引用:如果共享智能指针卷入了循环引用,资源都不会正常释放。(问题2)

为了解决循环引用,C++提供了另外一种智能指针:weak_ptr

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值