std::shared_ptr<X>
相对于原始的B*
有一些额外的开销。
shared_ptr<X>
基本上维护4个东西。它维护一个指向B
的指针,它维护两个引用计数(一个是“硬”引用计数,另一个是weak_ptr
的“软”引用计数),以及它维护一个清理函数。
清理函数是shared_ptr<X>
行为不同的原因。当您创建一个shared_ptr<X>
时,会创建一个调用该特定类型析构函数的函数,并将其存储在由shared_ptr<X>
管理的清理函数中。
当您更改所管理的类型(B*
变为C*
)时,清理函数保持不变。
因为shared_ptr<X>
需要管理引用计数,所以清理函数存储的额外开销是微不足道的。
对于unique_ptr<B>
,该类几乎与原始的B*
一样便宜。它除了B*
之外没有任何状态,并且其行为(在销毁时)归结为if (b) delete b;
。(是的,那个if (b)
是多余的,但优化器可以找出来)。
为了支持向基类转换和作为派生类删除,必须存储额外的状态,以记住unique_ptr
实际上是一个派生类。这可以采用存储指向删除器的指针的形式,例如shared_ptr
。
然而,这将使unique_ptr<B>
的大小翻倍,或者需要在堆上存储数据。
决定让unique_ptr<B>
没有开销,因此它不支持向基类转换并调用基类的析构函数。
现在,您可能可以通过添加删除器类型并存储知道正在销毁的东西类型的销毁函数来教会unique_ptr<B>
执行此操作。上述内容讨论了unique_ptr
的默认删除器,该删除器是无状态且微不足道的。
struct deleter {
void* state;
void(*f)(void*);
void operator()(void*)const{if (f) f(state);}
deleter(deleter const&)=default;
deleter(deleter&&o):deleter(o) { o.state = nullptr; o.f=nullptr; }
deleter()=delete;
template<class T>
deleter(T*t):
state(t),
f([](void*p){delete static_cast<T*>(p);})
{}
};
template<class T>
using smart_unique_ptr = std::unique_ptr<T, deleter>;
template<class T, class...Args>
smart_unique_ptr<T> make_smart_unique( Args&&... args ) {
T* t = new T(std::forward<Args>(args)...);
return { t, t };
}
这是一个实时示例,我会生成一个unique-ptr指向派生类,将其存储在unique-ptr指向基类中,然后重置基类。此时,派生指针被删除。
需要注意的是,在不更改deleter的情况下更改存储在此类unique-ptr中的指针将导致不良行为。另外,一个简单的
void(*)(void*)
deleter可能会遇到问题,因为传递的
void*
在基类和派生类之间的值可能会有所不同。