【问题标题】:Reference counting with a generic intrusive pointer client使用通用侵入式指针客户端进行引用计数
【发布时间】:2012-03-05 23:22:12
【问题描述】:

简介

Peter Weinhart 描述了如何设计a generic intrusive_ptr base class using CRTP,可以这样使用:

class foo : intrusive_base<foo>
{
     // foo-specific code.
};

这种方法强制要求所有foo 对象都带有一个引用计数器。假设我们有时按值保留foo,并且只想在我们有指针时支付引用计数器的价格。例如,有时我们想创建foo 实例并移动它们,有时我们想在堆上分配foo

从概念上讲,这个场景的正确机制是std::shared_ptr。但是,在某些情况下需要原始指针,这会调用侵入式指针,例如,当通过采用 void 指针的 C API 传递指针时。在这种情况下,在将指针传递给不透明 API 之前会“引用”指针,而在取回指针时会“取消引用”。

控制foo,可能最好的方法是使用基于策略的实现并拥有foo 的引用计数和基本版本。在无法控制foo 的情况下,另一种设计将反转继承关系:

template <typename Base>
class intrusive : public Base
{
    // ?

private:
    std::atomic_size_t ref_count_;   
};

typedef intrusive<foo> intrusive_foo;

// Assume boost::intrusive_ptr as intrusive pointer implementation
boost::intrusive_ptr<intrusive_foo> x = new intrusive_foo;
{
    auto y = x;   // Semantics: boost::intrusive_ptr_add_ref(x.get())

    // At scope exit: boost::intrusive_ptr_release(x.get())
}

在上面提到的文章中,Peter 说这样一个 “[intrusive] 的通用实现将利用 C++0x 可变参数模板和完美转发。”

问题

这样一个通用的intrusive 类的实现会是什么样子?我可以看到它可能会受益于 C++11 继承构造函数,但我不清楚实际上如何使用上述工具实现 intrusive 的主体。

【问题讨论】:

  • "假设我们有时按值保留foo,并且只想在我们有指针时支付引用计数器的价格?"然后不要使引用计数侵入。类型总是有其成员​​,无论这些成员是什么。除非你对它们进行堆分配,如果你这样做了,那么你也可以使用shared_ptr。如果您不想根据类型的使用方式为某物付费,这通常意味着“某物”不需要是该类的成员。
  • “在这种情况下,在将指针传递给不透明 API 之前会“引用”指针,而在取回指针时会“取消引用”。”使用非侵入式指针时,您几乎可以做同样的事情。只需在某个地方保留一个额外的 shared_ptr 实例,直到给定的指针返回。
  • @Nicol:从概念上讲,shared_ptr 确实是正确的选择。但是,我不能将它传递给采用void* 并手动增加引用计数的 C-API。当shared_ptr 超出范围并且C 库仍然有shared_ptr&lt;T&gt;::get() 的副本时,这是一个问题。我会澄清你在这个问题中的第一个担忧。
  • 对于需要传递给C API的对象,提供一个shared_ptr给自己作为成员。 C API 大概会让您知道原始指针何时完成,因此您可以释放shared_ptr。如果您的几个类以这种方式使用,请考虑将其设为基类(有点像std::enable_shared_from_this,它将weak_ptr 存储为对象本身的成员——除非您有一个shared_ptr 和它将是空的,除非您的对象处于原始、嗜血的 C 代码的贪婪紧握中)。
  • 这是解决问题的好方法。

标签: c++ c++11 variadic-templates crtp


【解决方案1】:

使用make_shared 可以获得与侵入式指针相同的效率。

在这种情况下,在将指针传递给不透明 API 之前会“引用”指针,而在取回指针时会“取消引用”。

正如其他人所说,您可以使用 enable_shared_from_this 从原始指针返回 shared_ptr(只要系统中的某个位置至少有一个 shared_ptr 仍然拥有该对象)

但要回答主要问题,我假设他的意思是使用可变参数模板和完美转发来定义构造函数,如下所示:

template <typename Base>
  class intrusive : public Base
  {
    template<typename... Args>
      intrusive(Args&&... args)
      : Base(std::forward<Args>(args)...), ref_count_(0)
      { }

这允许您使用任意数量的任何类型的参数构造intrusive,它们将被转发到Base,因此您可以使用可用于构造Base 的任何参数构造它。

另一种选择是使用 C++11 继承构造函数(在任何编译器 AFAIK 中都没有实现)

template <typename Base>
  class intrusive : public Base
  {
    using Base::Base;

【讨论】:

  • 这种形式的构造函数转发大概就是作者的意思吧。继承构造函数解决方案更好,但需要在类定义中正确初始化引用计数器。不幸的是,enable_shared_from_this 方法在我的情况下不起作用,因为我不能保证应用程序在将指针传递给 C API 后会保留共享指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-08
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多