如果您需要shared_ptr,请以unique_ptr 开头。然后建立起来。
struct cleanup_obj {
// not called with nullptr:
void operator()(obj* t)const {
obj_unref(t);
}
};
using obj_unique_ptr = std::unique_ptr<T, cleanup_obj>;
using obj_shared_ptr = std::shared_ptr<T>;
template<class T>
obj_unique_ptr<T> make_unique_refcount( T* t ) {
using ptr=obj_unique_ptr<T>;
if (!t) return ptr();
obj_ref(t);
return ptr(t);
}
template<class T>
obj_shared_ptr<T> make_shared_refcount( T* t ) {
return make_unique_refcount(t); // implicit convert does right thing
}
我做了什么?
首先,我写了一个unique_ptr包装器,因为我们还不如完整,它通过unique_ptr->shared_ptr隐式转换解决了shared_ptr的情况。
对于unique_ptr,我们不得不说我们没有使用默认的对象销毁器。在这种情况下,我们使用了一个无状态函数对象,它知道如何obj_unref 和obj*。无状态函数对象保持开销为零。
对于 null 的情况,我们不先添加引用,因为这很粗鲁。
对于shared_ptr,我们有一个有效的unique_ptr 使其成为免费功能。 shared_ptr 将愉快地存储 unique_ptr 拥有的驱逐舰功能。不必告诉它它有一个特殊的对象销毁器,因为shared_ptr 类型默认会擦除对象销毁。 (这是因为unique_ptr<T> 对裸指针的开销为零,而shared_ptr<T> 具有不可避免的引用计数块开销;设计人员认为,一旦您拥有该引用计数块,添加类型擦除的销毁函数就不是了真的很贵)。
请注意,我们的obj_unique_ptr<T> 在裸指针上也是零开销。很多时候,您会想要其中之一而不是共享的。
现在,如果您愿意,您可以将 obj_unique_ptr 升级为完整的侵入式指针,其开销低于 shared_ptr。
template<class T>
struct obj_refcount_ptr : obj_unique_ptr<T> // public
{
// from unique ptr:
obj_refcount_ptr(obj_unique_ptr<T> p):obj_unique_ptr<T>(std::move(p)){}
obj_refcount_ptr& operator=(obj_unique_ptr<T> p){
static_cast<obj_unique_ptr<T>&>(*this)=std::move(p);
return *this;
}
obj_refcount_ptr(obj_refcount_ptr&&)=default;
obj_refcount_ptr& operator=(obj_refcount_ptr&&)=default;
obj_refcount_ptr()=default;
obj_refcount_ptr(obj_refcount_ptr const& o):
obj_refcount_ptr(make_unique_refcount(o.get())
{}
obj_refcount_ptr& operator=(obj_refcount_ptr const& o) {
*this = make_unique_refcount(o.get());
return *this;
}
};
我认为涵盖了它。现在它是一个零开销的引用计数侵入式智能指针。这些侵入式智能指针可以通过隐式转换转换为std::shared_ptr<T>,因为它们仍然是unique_ptrs。他们只是 unique_ptrs 我们教过的模仿自己!
确实需要从obj_refcount_ptr 转移到shared_ptr。我们可以解决这个问题:
operator std::shared_ptr<T>() const {
return obj_refcount_ptr(*this);
}
创建*this 的obj_refcount_ptr 副本并将其移动到shared_ptr。只有一个 add ref 被调用,而 remove ref 仅在 shared_ptr 计数变为零时被调用。
一般的方法是从最简单的智能指针 (unique_ptr) 开始,把它弄好,然后利用它的实现来得到shared_ptr,最终得到refcount_ptr。我们可以单独测试unique_ptr的实现,它的正确性使得测试更丰富的指针更容易。