shared_ptr魔术:)

Arm*_*yan 82 c++ destructor smart-pointers

Lidström先生和我有一个争论 :)

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

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

DanielLidström:" shared_ptr使用自己的析构函数来删除Concrete实例.这在C++社区中被称为RAII.我的建议是你学习RAII的全部内容.它将使你的C++编码在使用时更加容易RAII在所有情况下."

Armen Tsirunyan:"我知道RAII,我也知道,当pn达到0时,最终shared_ptr析构函数可能会删除存储的px.但是如果px有静态类型指针Base和动态类型指针Derived,那么除非Base有一个虚拟析构函数,会导致不确定的行为.如果我错了,请纠正我."

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

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

sel*_*tze 70

是的,可以通过这种方式实现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);
   ...
};
Run Code Online (Sandbox Code Playgroud)

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

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

int main() {
   shared_ptr<Base> sp (new Derived);
}
Run Code Online (Sandbox Code Playgroud)

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

关于这个构造函数(20.7.2.2.1),C++ 11标准有以下内容:

要求: p必须可转换为T*.Y应该是一个完整的类型.表达式delete p应格式良好,具有明确的行为,不得抛出异常.

效果:构造一个shared_ptr对象拥有指针p.

...

对于析构函数(20.7.2.2.2):

效果:如果*this或与另一个shared_ptr实例共享所有权(use_count() > 1),则没有副作用.否则,如果*this拥有一个对象p和一个删除器d,d(p)则被调用. 否则,如果*this拥有一个指针p,delete p则被调用.

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


ybu*_*ill 28

创建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);
Run Code Online (Sandbox Code Playgroud)

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

  • 关于不完整类型的注释+1,在使用`shared_ptr`作为属性时非常方便. (4认同)

Art*_*yom 16

只是,

shared_ptr 使用由构造函数创建的特殊删除函数,该函数总是使用给定对象的析构函数而不是Base的析构函数,这对模板元编程有一些作用,但它有效.

这样的事情

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}
Run Code Online (Sandbox Code Playgroud)

  • @ paul_71:我同意你的看法.另一方面,我相信这个讨论不仅对我有用,而且对于那些不了解shared_ptr这个事实的人也有用.所以我想无论如何都要开始这个线程并不是一个大罪恶:) (6认同)
  • @Armen当然不是.相反,你在指向shared_ptr <T>这个非常非常重要的功能方面做得很好,即使有经验的c ++开发人员也经常监督它. (3认同)