如果理解了侵入式容器,侵入式智能指针也很容易理解。传统的智能指针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_shared
和std::enable_shared_from_this
。
参考:C++智能指针 3 :内存布局(非侵入式、enable_shared_from_this & 侵入式)
Q. E. D.