如何使用shared_ptr来管理已经重新计数的托管对象?

时间:2017-03-10 11:54:36

标签: c++ c++11 memory

如果对象已经被引用计数(如C中的glib),则obj_refobj_unref。我们只有一个像obj *p这样的指针。

我们如何使用c ++' shared_ptr来管理对象,以便我们可以拥有统一的界面。

好吧,似乎有很多人误解了我的意图。

这里最大的问题不是删除。关于通知原始经理我增加了引用数量。

如果我分配或复制,只有std::shared_ptr增加了引用计数,但原始引用没有。反正有通知吗?所以作为unref操作。

5 个答案:

答案 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_unrefobj*。无状态函数对象将开销保持为零。

对于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)

在多个智能指针实现中拥有相同的对象是一个非常糟糕的主意,因为它们的引用计数无法相互了解。一旦引用计数在一个中达到零,它将删除该对象,即使另一个仍保留引用。

如果你真的不得不用自己构建智能指针(不做任何事情),但我真的不推荐这种做法。

选择一个实现并坚持下去。