【发布时间】: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<T>::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