侵入式智能指针 boost::intrusive_ptr

作者: , 共 1991 字 , 共阅读 0

如果理解了侵入式容器,侵入式智能指针也很容易理解。传统的智能指针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.

类似文章:
编程 » C++, Boost, 数据容器
Boost.Intrusive 是一个很有意思的实现,里面实现了很多侵入式容器,在特定环境下,可以大大提升性能。
编程 » C++, 智能指针
前面已经提到std::shared_ptr有三个缺陷:
智能指针在现代 C++里用得越多。以前只知道它大致的原理,比如使用引用计数。但很多实现细节并不清楚,最直接的,它是如何实现多线程安全的?今天找了 gnu c++ library 的实现好好看了一下。
编程 » C++, 智能指针
理论上而言,当 C++提供了std::unique_ptr, C++的程序就不应该出现普通指针了。所有普通指针都可以用std::unique_ptr代替,避免手动删除对象。
编程 » C++, 异步
C++11 的标准异步库至少包含下面内容:
相似度: 0.120
boost是除std外最常用的 C++库,覆盖很多常用操作。目前最新的版本是1.59.0http://boost.org/上可以查看最新版本号,将下面的 59 换成最新的即可)。
由 Facebook 开发和维护的 C++库 Folly 提供了锁folly::MicroLock,代码文件地址:https://github.com/facebook/folly/blob/master/folly/MicroLock.h
编程 » folly, C++, 数据容器
由 Facebook 开发和维护的 C++库 Folly 提供folly::small_vector,代码文件地址:https://github.com/facebook/folly/blob/master/folly/small_vector.h
编程 » C++, boost, 命令行
我们用boost的命令行库program_options解析命令行,在解释布尔命令行时使用下面代码:
编程 » C++, C++标准库
std::tuple的原理并不复杂,但有些细节非常有意思。其中有一个是至少在gnu C++ std的实现中,std::tuple是倒序存储的:
编程 » C++, Boost, 数据容器
Boost.Intrusive 是一个很有意思的实现,里面实现了很多侵入式容器,在特定环境下,可以大大提升性能。
编程 » C++, 智能指针
前面已经提到std::shared_ptr有三个缺陷: