欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++11智能指针之shared_ptr

程序员文章站 2024-03-14 11:37:22
...

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的值得到引用计数。

C++11智能指针之shared_ptr

析构

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

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

C++11智能指针之shared_ptr

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

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

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

C++11智能指针之shared_ptr

避免这个问题,尽量不要从一个裸指针(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;
}

C++11智能指针之shared_ptr

分析:什么都没有输出!

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

C++11智能指针之shared_ptr
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;
}

C++11智能指针之shared_ptr

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

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

相关标签: C++