19

侵入式智能指针 boost::intrusive_ptr

 3 years ago
source link: https://zhiqiang.org/coding/boost-intrusive-ptr.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

侵入式智能指针 boost::intrusive_ptr

作者: 张志强

, 发表于 2020-01-11

, 共 1995 字 , 共阅读 174 次

如果理解了侵入式容器,侵入式智能指针也很容易理解。传统的智能指针std::shared_ptr使用了和数据无关的引用计数,这带来两个问题:

  • 引用计数要在shared_ptr对象间共享,所以它只能位于堆上。这使得每次都需要重新new一小块内存。这导致性能问题。
  • 引用计数的内存区域和数据区域不一致,缓存失效导致性能问题。
  • 编写代码不善,将导致同一个数据,绑定到了两个引用计数,从而导致双重删除问题。典型代码如下:
int* x = new int;
std::shared_ptr<int> x1 = x;
std::shared_ptr<int> x2 = x;

侵入式智能指针试图解决这些问题,方法也特别直接,那就是将引用计数直接塞进数据本身,和数据共存亡。从用户角度,这只需要继承boost::intrusive_ptr_base基类:

struct T : public boost::intrusive_ptr_base<T> { 
    int age;
    std::string name;
};

T* t = new T();

// 下面这么些很安全,而且速度很快!
boost::intrusive_ptr<T> x1 =t;
boost::intrusive_ptr<T> x2 =t;

我们看boost::intrusive_ptr_base的定义,其核心就是增加一个原子操作的引用计数,以及自增和自减引用计数的函数:

template<class T>
class boost::intrusive_ptr_base {
public:
    intrusive_ptr_base() : ref_count(0) {}

    friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* p) {
        ++p->ref_count;
    }

    friend void intrusive_ptr_release(intrusive_ptr_base<T> const* p) {
        if (--p->ref_count == 0) {
            boost::checked_delete(static_cast<T const*>(s));
        }
    }

    boost::intrusive_ptr<T> self() {
        return boost::intrusive_ptr<T>((T*)this);
    }

private:
    mutable boost::detail::atomic_count ref_count;
};

接下来实际的boost::intrusive_ptr的定义就很简单了:

template<class T> 
class boost::intrusive_ptr {
public:
    intrusive_ptr(T* p, bool add_ref = true) : px(p) {
        if (px != 0 && add_ref) {
            intrusive_ptr_add_ref(px);
        }
    }

    ~intrusive_ptr() {
        if (px != 0) {
            intrusive_ptr_release(px);
        }
    }

private:
    T * px;
}

由于解决了std::shared_ptr的三个问题,boost::intrusive_ptr的效率更高。但它也有缺陷。除了侵入式设计之外,最重要的是boost::intrusive_ptr无法支持weak_ptr,从而无法解决环形引用问题。这时因为std::shared_ptr里的数据对象和计数器分离,可以有不同的生命周期。shared_ptr的引用计数里有两个计数对象,从而支持weak_ptr

另外一个方面,std::shared_ptr也意识到了其性能问题,内部也提供机制解决这些问题,其核心便是std::make_sharedstd::enable_shared_from_this

参考:C++智能指针 3 :内存布局(非侵入式、enable_shared_from_this & 侵入式)

Q. E. D.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK