shared_ptr魔术:)

时间:2010-10-10 09:41:02

标签: c++ destructor smart-pointers

Mr. Lidström and I had an argument:)

先生。 Lidström的主张是构造shared_ptr<Base> p(new Derived);不要求Base有虚拟析构函数:

  

Armen Tsirunyan :“真的吗? shared_ptr 会正确清理吗?在这种情况下你能否证明如何实现这种效果?”

     

DanielLidström:“ shared_ptr 使用自己的析构函数来删除Concrete实例。这在C ++社区中称为RAII。我的建议是你学习所有您可以了解RAII。当您在所有情况下使用RAII时,它将使您的C ++编码变得更加容易。“

     

Armen Tsirunyan :“我知道RAII,我也知道,当pn达到0时, shared_ptr 析构函数最终可能会删除存储的px。但是如果px有静态的话键入指向Base的指针和指向Derived的动态类型指针,然后除非Base具有虚拟析构函数,否则将导致未定义的行为。如果我错了,请纠正我。“

     

DanielLidström:“ shared_ptr 知道静态类型是Concrete。它知道这个,因为我在它的构造函数中传递它!看起来有点像魔法,但我可以保证你的设计非常好。“

所以,判断我们。如何实现 shared_ptr 而不需要多态类具有虚拟析构函数是否可能(如果是)? 提前致谢

3 个答案:

答案 0 :(得分:69)

是的,可以通过这种方式实现shared_ptr。 Boost确实如此,C ++ 11标准也需要这种行为。作为一种额外的灵活性,shared_ptr管理的不仅仅是参考计数器。通常将所谓的删除器放入同样包含参考计数器的存储器块中。但有趣的是,此删除器的类型不是shared_ptr类型的一部分。这称为“类型擦除”,基本上与用于实现“多态函数”boost :: function或std :: function的技术相同,用于隐藏实际的仿函数类型。为了使您的示例有效,我们需要一个模板化的构造函数:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

所以,如果你在你的类Base和Derived中使用它......

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

...使用Y = Derived的模板化构造函数用于构造shared_ptr对象。因此,构造函数有机会创建适当的删除对象和引用计数器,并将指向该控制块的指针存储为数据成员。如果引用计数器达到零,则将使用先前创建的和Derived-aware删除器来处置该对象。

C ++ 11标准有关于此构造函数的说明(20.7.2.2.1):

  

要求: p必须可转换为T*Y应为完整类型。 表达式delete p应格式正确,行为应明确,不得抛出异常。

     

效果:构造拥有指针shared_ptr的{​​{1}}对象

     

...

对于析构函数(20.7.2.2.2):

  

效果:如果p 为空或与另一个*this实例(shared_ptr)共享所有权,则没有任何一方效果。   否则,如果use_count() > 1拥有对象*this和删除者p,则会调用d。   否则,如果d(p)拥有指针*this,则会调用p

(强调使用粗体字体是我的。)

答案 1 :(得分:26)

创建shared_ptr时,它会在其自身内存储删除器对象。当shared_ptr即将释放指向的资源时,将调用此对象。由于您知道如何在构造点销毁资源,因此可以将shared_ptr用于不完整类型。创建shared_ptr的人在那里存储了正确的删除器。

例如,您可以创建自定义删除器:

void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

p将调用DeleteDerived来销毁指向的对象。实现会自动执行此操作。

答案 2 :(得分:14)

简单地说,

shared_ptr使用由始终使用的构造函数创建的特殊删除函数 给定对象的析构函数而不是Base的析构函数,这是模板元编程的一些工作,但它可以工作。

像这样的东西

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}