一.  unique_ptr

这种指针刚好和 shared_ptr 相反: unique_ptr 禁止了拷贝构造函数, 因此任何时刻不可能有两个 unique_ptr 指向同一个对象, 也就是说它没有复制操作. 那么要将指针赋给另一个指针怎么办呢? 这就要用 std::move 函数来移交所有权, 称为移动操作.  创建操作最好用 C++14 引入的 std::make_unique 替代new, 因为这可以让编译器产生更小更快的代码. 释放操作主要有两个函数: release 释放所有权并返回原始指针, reset 不带参数的话即释放所有权, 带参数的话则还能绑定到新对象.

pointer release();
void reset( pointer _Ptr = pointer() );

unique_ptr 虽然不能复制, 但在函数中作为返回值却可以这样用:

unique_ptr<int> GetId() 
{
   unique_ptr<int> p(new int(1));
   return p;
}
unique_ptr<int> pID = GetId();

unique_ptr有个重要用处是它可以用作stl容器元素, 但不能直接push_back, 而是需要用move移交所有权的方式, 如下

   unique_ptr<int> p(new int(2));
   vector<unique_ptr<int>> v;
   v.push_back(std::move(p));

另一个重要用处是编写异常安全的代码

void fun()
{
   unique_ptr<int> p(new int(2));  //即使下面发生异常的话,也能确保对象被销毁
   show(p);
   //...
}
   

二. shared_ptr

用vc2008的话, 需要安装sp1后这样使用它std::tr1::shared_ptr, 若是使用新版本比如vc2015则不用.
shared_ptr创建操作最好用C++11引入的 std::make_shared 替代new; 若要获得普通指针可用get函数; 释放所有权是用reset函数而无release函数; 交换两个shared_ptrswap函数

void reset() 
{    
   shared_ptr().swap(*this); // release resource and convert to empty shared_ptr object
}

要注意防止从一个普通指针构造出两个以上的智能指针, 比如这样写是错误的

   int *p = new int(2);
   shared_ptr<int> p1(p);
   shared_ptr<int> p2(p); //因为使用了新引用计数器, 故会发生第二次销毁

若要对指针执行强制类型转换比如static_cast, 我们可以取出普通指针然后进行强制转换, 但不要把它存到另一个shared_ptr里, 因为新的 shared_ptr会有独立的引用计数器, 从而发生上面那种错误。对于这种需要强制转换的情形, 就要用到 static_pointer_cast 或者 dynamic_pointer_cast 才能维护正确的引用计数

   //假设CBox继承自CObject
   shared_ptr<CBase> p(new CBox);
   shared_ptr<CBox> pb = static_pointer_cast<CBox>(p);

在定义类时要注意防止类对象本身当中不能存储类对象本身的 shared_ptr, 否则这些对象永远都不会被销毁, 同样也要防止使用shared_ptr造成对象互相依赖的情形, 即类A中定义了指向类B对象的shared_ptr, 类B中也定义了指向类A对象的shared_ptr, 那么当我们为这两个类创建出对象后, 它们都不能被销毁了, 其本质原因是能最终触发对象销毁的两个智能指针恰好是两个对象内的成员, 从而使两对象的生存期构成环形依赖关系.

class B;
class A
{
public:
   shared_ptr<B> pb;

   ~A()
   {
      cout << "A end\n";
   }
};

class B
{
public:
   shared_ptr<A> pa;
   
   ~B()
   {
      cout << "B end\n";
   }
};

 三. weak_ptr

上面谈到shared_ptr 的循环引用会造成对象无法被释放从而导致内存泄漏, 而要相互保存对方的智能指针怎么办呢, 这就要用weak_ptr, 它相当于 shared_ptr 的观察者, 不会增加指针的引用计数, 不会去管理对象的生存期, 从而不会导致循环的依赖链.

在需要shared_ptr的时候用lock()函数; 用函数use_count()可以观测引用计数, 函数expired()等价于 use_count()==0 用于判断对象是否已释放

 

C++
2015-04-07 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *