C++11智能指针(shared_ptr)

智能指针在C++11版本之后提供,包含在头文件中,
shared_ptr、unique_ptr、weak_ptr

shared_ptr的使用

shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数(reference counting),每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
2.通过make_shared函数得到
3.通过另外一个智能指针初始化

    std::shared_ptr bptr(p);//方式1 
    std::shared_ptr aptr = std::make_shared(2);//方式 2
    std::shared_ptr cptr(aptr);  //方式3

shared_ptr 的一些注意事项

转自https://blog.csdn.net/man_sion/article/details/77196766

  1. 禁止纯指针给智能指针赋值或者拷贝构造。

int* a=new int(2);
shared_ptrsp=a;// error
sp=a;// error

2. shared_ptr多次引用同一数据,会导致两次释放同一内存。如下:

{
int* pInt = new int[100];
shared_ptr sp1(pInt);
// 一些其它代码之后…
shared_ptr sp2(pInt);
}

3.使用shared_ptr包装this指针带来的问题,如下:

class tester 
{
public:
  tester()
  ~tester()
  {
    std::cout << "析构函数被调用!\n"; 
  }
public:
  shared_ptr sget()
  {
    return shared_ptr(this);
  }
};

int main()
{
  tester t;
  shared_ptr sp =  t.sget(); // …
  return 0;
}

也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码:

class tester : public enable_shared_from_this
{
public:
  tester()
  ~tester()
  {
  std::cout << "析构函数被调用!\n"; 
  }
public:
 shared_ptr sget()
  {
  return shared_from_this();
  }
};

int main()
{
  shared_ptr sp(new tester);
  // 正确使用sp 指针。
  sp->sget();
  return 0;
}

enable_shared_from_this:

std::enable_shared_from_this 能让一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。

若一个类 T 继承 std::enable_shared_from_this ,则会为该类 T 提供成员函数: shared_from_this 。 当 T 类型对象 t 被一个为名为 pt 的 std::shared_ptr 类对象管理时,调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr 对象,它与 pt 共享 t 的所有权。

当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。

a.为何不直接传递this指针

使用智能指针的初衷就是为了方便资源管理,如果在某些地方使用智能指针,某些地方使用原始指针,很容易破坏智能指针的语义,从而产生各种错误。

b.可以直接传递share_ptr么?

答案是不能,因为这样会造成2个非共享的share_ptr指向同一个对象,未增加引用计数导对象被析构两次。

c.为何会出现这种使用场合

因为在异步调用中,存在一个保活机制,异步函数执行的时间点我们是无法确定的,然而异步函数可能会使用到异步调用之前就存在的变量。为了保证该变量在异步函数执期间一直有效,我们可以传递一个指向自身的share_ptr给异步函数,这样在异步函数执行期间share_ptr所管理的对象就不会析构,所使用的变量也会一直有效了(保活)。

4. shared_ptr循环引用导致内存泄露,代码如下:

typedef shared_ptr parent_ptr;
typedef shared_ptr child_ptr; 

class parent
{
public:
       ~parent() { 
              std::cout <<"父类析构函数被调用.\n"; 
       }
public:
       child_ptr children;
};

class child
{
public:
       ~child() { 
              std::cout <<"子类析构函数被调用.\n"; 
       }
public:
       parent_ptr parent;
};

int main()
{
  parent_ptr father(new parent());
  child_ptr son(new child);
  // 父子互相引用。
  father->children = son;
  son->parent = father;
  return 0;
}

如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。

最后

没有std::shared_ptr.所以shared_ptr只能管理单个对象,而不能管理对象数组。

你可能感兴趣的:(C++11智能指针(shared_ptr))