如果对象已经被引用计数(如C中的glib),则obj_ref
,obj_unref
。我们只有一个像obj *p
这样的指针。
我们如何使用c ++' shared_ptr
来管理对象,以便我们可以拥有统一的界面。
好吧,似乎有很多人误解了我的意图。
这里最大的问题不是删除。关于通知原始经理我增加了引用数量。
如果我分配或复制,只有std::shared_ptr
增加了引用计数,但原始引用没有。反正有通知吗?所以作为unref操作。
答案 0 :(得分:2)
std::shared_ptr
允许您传递自定义删除器,该删除器应在销毁拥有对象时调用。您可以使用它来拨打obj_unref
。
obj* p = create_obj();
p->obj_ref();
std::shared_ptr<obj> sp(p, [](auto p) {
p->obj_unref();
});
/* use sp normally, obj will be 'obj_unref'ed and deleted when sp goes out of scope */
我不知道obj
是如何创建的,以及当计数达到0时它是否被obj_unref()
销毁,但我希望你明白我的意思。
我们的想法是在开始时仅增加obj
个内部引用计数一次,并在最后一个shared_ptr
被销毁时减少一次。
答案 1 :(得分:1)
请勿尝试以某种方式将录像带std::shared_ptr
重新计算到您的自定义录像带,但这不会很好地结束。只需编写一个自定义指针:
struct objPtr {
objPtr()
: _ptr{nullptr} { }
objPtr(obj *ptr)
: _ptr{ptr} {
if(_ptr)
_ptr->obj_ref();
}
~objPtr() {
if(_ptr)
_ptr->obj_unref();
}
objPtr(objPtr const &orig)
: objPtr{orig._ptr} { }
objPtr &operator = (objPtr const &orig) {
obj *const oPtr = std::exchange(_ptr, orig._ptr);
_ptr->obj_ref();
oPtr->obj_unref();
return *this;
}
obj &operator * () { return *_ptr; }
obj const &operator * () const { return *_ptr; }
obj *operator -> () { return _ptr; }
obj const *operator -> () const { return _ptr; }
operator bool() const { return _ptr; }
bool operator ! () const { return !_ptr; }
private:
obj *_ptr;
};
如果您愿意,可以添加移动构造和分配。
答案 2 :(得分:1)
如果您需要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-&gt; 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_ptr
。他们只是unique_ptr
我们已经教过自己复制了!
确实需要从obj_refcount_ptr
移动才能获得shared_ptr
。我们可以解决这个问题:
operator std::shared_ptr<T>() const {
return obj_refcount_ptr(*this);
}
创建obj_refcount_ptr
的{{1}}副本,并将其移至*this
。只调用一个add ref,并且仅在shared_ptr
计数变为零时调用remove ref。
一般方法是从最简单的智能指针(shared_ptr
)开始,正确使用,然后利用其实现来获取unique_ptr
并最终获得shared_ptr
。我们可以单独测试refcount_ptr
实现,它的正确性使得更容易测试更丰富的指针。
答案 3 :(得分:0)
最简单的方法,即破坏性最小的方法,就是简单地为对象编写自己的外观,将底层对象作为私有成员,并提供简单的包装来访问它。
然后使用std::shared_ptr
。
答案 4 :(得分:-1)
在多个智能指针实现中拥有相同的对象是一个非常糟糕的主意,因为它们的引用计数无法相互了解。一旦引用计数在一个中达到零,它将删除该对象,即使另一个仍保留引用。
如果你真的不得不用自己构建智能指针(不做任何事情),但我真的不推荐这种做法。
选择一个实现并坚持下去。