Boost智能指针小记

参考资料:http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/smart_ptr.htm

[scoped_ptr]

模板类scoped_ptr储存着一个指向动态分配的对象的指针,可以通过reset方法显式释放对象,或者在scoped_ptr的析构函数中释放。

Scoped_ptr提供了基本的RAII机制,没有shared-ownership或transfer-of-ownership的语义问题。它的名字和语义表明了它的目的只是单纯地在当前作用域保持所有权,同时它继承于noncopyable。

由于scoped_ptr只是用于简单需求的简单解决方案,它本身设计就很简单,这使得它的操作跟内置指针一样快,并且不需要耗费额外的存储空间。

此外,scoped_ptr不能用于STL容器类和指向动态分配的数组的指针,如果需要,可以使用shared_ptr和scoped_array。一个比较经常的用法就是使用scoped_ptr来实现pimpl,避免在头文件中引入类的完整实现,可以减少编译时间等。

以下是scoped_ptr的概览:

namespace boost {
 
    template<class T> class scoped_ptr : noncopyable {
 
    public:
       typedef T element_type;
 
       explicit scoped_ptr(T * p = 0); //never throws
       ~scoped_ptr(); //never throws
 
       void reset(T * p = 0); //never throws
 
       T & operator*() const; //never throws
       T * operator->() const; //never throws
       T * get() const; //never throws
 
       operator unspecified-bool-type() const; //never throws
 
       void swap(scoped_ptr & b); //never throws
    };
 
    template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); //never throws
 
}

以下是scoped_ptr的使用示例:

#include <boost/scoped_ptr.hpp>
#include <iostream>
 
class MyClass
{
public:
    MyClass(){ std::cout << "MyClass" << std::endl; }
    ~MyClass(){ std::cout << "~MyClass" << std::endl; }
 
    void MyFunc(){ std::cout << "MyFunc" << std::endl; }
};
 
int main(int argc, char *argv[])
{
    boost::scoped_ptr<MyClass> spMyClass(new MyClass);
    spMyClass->MyFunc();
    spMyClass.reset();
 
    system("Pause");
    return 0;
}

[shared_ptr]

顾名思义,shared_ptr智能指针能够共享所有权,并且它符合C++标准库中CopyConstructible和Assignable的要求,所以它可以用于STL的容器类上。通常,shared_ptr不能正确地管理数组指针,如果需要,可以使用shared_array。

智能指针shared_ptr通过引用计数来管理资源,所以存在循环引用的问题,可以通过weak_ptr来打破循环。在没有看源码前,可以思考下如何实现shared_ptr的引用计数,这对实践是有帮助的(显然是废话)。

下面两种初始化方式中,第一种是得到提倡的:

    boost::shared_ptr<MyClass> spMyClass1(new MyClass);
 
    MyClass *pMyClass = new MyClass();
    boost::shared_ptr<MyClass> spMyClass2(pMyClass);

如果使用第二种,要避免出现如下代码,存在由于重复释放导致访问非法内存的错误:

    MyClass *pMyClass = new MyClass();
    boost::shared_ptr<MyClass> spMyClass1(pMyClass);
    boost::shared_ptr<MyClass> spMyClass2(pMyClass);

正确的做法应该是:

    MyClass *pMyClass = new MyClass();
    boost::shared_ptr<MyClass> spMyClass1(pMyClass);
    boost::shared_ptr<MyClass> spMyClass2(spMyClass1);

这个可能在实践中出现的问题就与理解引用计数怎么实现的相关联。

此外,使用一个临时shared_ptr作为函参也可能出现问题,导致内存泄漏。因为C++参数的求值顺序是未定的(未求证,是没有相关标准还是编译器厂商自定义了?),比如下述代码的构造顺序可能为new int(2),g(),shared_ptr,但是g()如果抛出异常呢?那么shared_ptr就构造不了,new了一个int,就没有释放了。

f(shared_ptr<int>(new int(2)), g());

[weak_ptr]

前面说过shared_ptr存在循环引用的问题,使用weak_ptr可以解决。智能指针weak_ptr只能通过shared_ptr来构造,它可以管理shared_ptr指向的对象,同时不增加引用计数。

智能指针weak_ptr所能进行的操作是有限的,因为在多线程编程中使用它来获取存储的指针通常都是危险的,即便在单线程中,也可能引发问题,比如boost官网上展示的一段代码:

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
 
if(int * r = q.get())
{
    //use *r
}

存在执行q.get()后,另一个线程中指向相同对象的shared_ptr执行了reset的可能,对于这个问题,可以如下解决(同样来自官方):

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
 
if(shared_ptr<int> r = q.lock())
{
    //use *r
}

成员函数lock()返回如下:

expired()? shared_ptr<T>(): shared_ptr<T>(*this)

而expired()函数返回如下:

use_count() == 0

[intrusive_ptr]

之前说过shared_ptr存在一个问题,就是多次使用未经过包装的原生指针(raw pointer)进行构造,会导致多次析构,造成非法访问内存的错误。这是由于shared_ptr把引用计数放在shared_ptr结构中,所以使用原生指针构造会造成多份引用计数。

而intrusive_ptr则不存在这个问题,因为它把引用计数放在管理的对象中。同时,它要求对象必须有intrusive_ptr_add_ref和intrusive_ptr_release两个成员函数以供intrusive_ptr调用来管理引用计数。这也是intrusive_ptr的名称由来。

通常来说,如果使用intrusive_ptr的好处不是很明显,那么就优先考虑shared_ptr。



你可能感兴趣的:(多线程,存储,Class,iostream,编译器,RAII)