简体   繁体   English

C++11 中具有虚拟成员的虚拟析构函数

[英]Virtual destructor with virtual members in C++11

In these slides about C++11/14 standard, on slide 15, the author writes that "many classic coding rules [are] no longer applicable" in C++11. 在这些关于 C++11/14 标准的幻灯片中,在幻灯片 15 上,作者写道 C++11 中“许多经典的编码规则 [不再适用]”。 He proposes a list of three examples, and I agree with the Rule of Three and the memory management.他提出了一个包含三个例子的列表,我同意三法则和内存管理。

However his second example is "Virtual destructor with virtual members" (just that).然而,他的第二个例子是“具有虚拟成员的虚拟析构函数”(仅此而已)。 What does it mean?这是什么意思? I know one must declare as virtual the base class destructor in order to call the right destructor if we have something like我知道必须将基类析构函数声明为 virtual 才能调用正确的析构函数,如果我们有类似的东西

Base *b = new Derived;
...
delete b;

This is well explained here: When to use virtual destructors?这在这里得到了很好的解释: 何时使用虚拟析构函数?

But is it useless now in C++11 to declare virtual your destructor if you have virtual members?但是,如果您有虚拟成员,现在在 C++11 中声明 virtual 您的析构函数是没有用的吗?

As the author of the slides I'll try to clarify.作为幻灯片的作者,我会尽力澄清。

If you write code explicitly allocating a Derived instance with new and destroying it with delete using a base class pointer then you need to define a virtual destructor, otherwise you end up with incompletely destroying the Derived instance.如果您编写代码使用new显式分配Derived实例并使用基类指针使用delete销毁它,那么您需要定义一个virtual析构函数,否则您最终会不完全销毁Derived实例。 However, I recommend to abstain from new and delete completely and use exclusively shared_ptr for referring to heap-allocated polymorphic objects, like但是,我建议完全避免使用newdelete并专门使用shared_ptr来引用堆分配的多态对象,例如

shared_ptr<Base> pb=make_shared<Derived>();

This way, the shared pointer keeps track of the original destructor to be used, even if shared_ptr<Base> is used to represent it.这样,共享指针会跟踪要使用的原始析构函数,即使使用shared_ptr<Base>来表示它也是如此。 Once, the last referring shared_ptr goes out of scope or is reset, ~Derived() will be called and the memory released.一旦最后引用的shared_ptr超出范围或被重置, ~Derived()将被调用并释放内存。 Therefore, you don't need to make ~Base() virtual.因此,您不需要将~Base()设为虚拟。

unique_ptr<Base> and make_unique<Derived> do not provide this feature, because they don't provide the mechanics of shared_ptr with respect to the deleter , because unique pointer is much simpler and aims for the lowest overhead and thus is not storing the extra function pointer needed for the deleter. unique_ptr<Base>make_unique<Derived>不提供此功能,因为它们不提供有关删除器shared_ptr机制,因为唯一指针要简单得多并且旨在实现最低开销,因此不存储额外的删除器所需的函数指针。 With unique_ptr the deleter function is part of the type and thus a uniqe_ptr with a deleter referring to ~Derived would not be compatible with a unique_ptr<Base> using the default deleter, which would be wrong for a derived instance anyway, if ~Base wasn't virtual.对于unique_ptr ,删除器函数是类型的一部分,因此带有引用~Derived的删除器的 uniqe_ptr 将与使用默认删除器的unique_ptr<Base>不兼容,这对于派生实例无论如何都是错误的,如果~Base不是虚拟的。

The individual suggestions I make, are meant to be easy to follow and followed all together.我提出的个人建议旨在易于遵循并一起遵循。 They try to produce simpler code, by letting all resource management be done by library components and the compiler generated code.他们试图通过让所有资源管理由库组件和编译器生成的代码来完成,从而生成更简单的代码。

Defining a (virtual) destructor in a class, will prohibit a compiler-provided move constructor/assignment operator and might prohibit also a compiler provided copy constructor/assignment operator in future versions of C++.在类中定义(虚拟)析构函数将禁止编译器提供的移动构造函数/赋值运算符,并且在未来的 C++ 版本中也可能禁止编译器提供的复制构造函数/赋值运算符。 Resurrecting them has become easy with =default , but still looks like a lot of boilerplate code.使用=default恢复它们变得很容易,但看起来仍然像很多样板代码。 And the best code is the code you don't have to write, because it can not be wrong (I know there are still exceptions to that rule).最好的代码是您不必编写的代码,因为它不会出错(我知道该规则仍有例外)。

To summarize "Don't define a (virtual) destructor" as a corollary to my "Rule of Zero":总结“不要定义(虚拟)析构函数”作为我的“零规则”的必然结果:

Whenever you design a polymorphic (OO) class hierarchy in modern C++ and want/need to allocate its instances on the heap and access them through a base class pointer use make_shared<Derived>() to instantiate them and shared_ptr<Base> to keep them around.每当您在现代 C++ 中设计多态 (OO) 类层次结构并希望/需要在堆上分配其实例并通过基类指针访问它们时,请使用make_shared<Derived>()来实例化它们并使用shared_ptr<Base>来保留它们大约。 This allows you to keep the "Rule of Zero".这使您可以保持“零规则”。

This doesn't mean you must allocate all polymorphic objects on the heap.这并不意味着您必须在堆上分配所有多态对象。 For example, defining a function taking a (Base&) as parameter, can be called with a local Derived variable without problems and will behave polymorphic, with respect to virtual member functions of Base .例如,定义一个以(Base&)作为参数的函数,可以毫无问题地使用局部Derived变量调用,并且对于Base虚拟成员函数而言,它的行为是多态的。

In my opinion dynamic OO polymorphism is heavily overused in many systems.在我看来,动态 OO 多态性在许多系统中被过度使用。 We shouldn't program like Java, when we use C++, unless we have a problem, where dynamic polymorphism with heap allocated objects is the right solution.当我们使用 C++ 时,我们不应该像 Java 那样编程,除非我们遇到问题,其中堆分配对象的动态多态是正确的解决方案。

I think that this is to do with the "rule of zero" mentioned elsewhere in the presentation.我认为这与演示文稿中其他地方提到的“零规则”有关。

If you only have automatic member variables (ie use shared_ptr or unique_ptr for members that would otherwise be raw pointers) then you don't need to write your own copy or move constructors, or assignment operators -- the compiler-provided defaults will be optimal.如果您只有自动成员变量(即使用shared_ptrunique_ptr作为原始指针的成员),那么您不需要编写自己的复制或移动构造函数或赋值运算符——编译器提供的默认值将是最佳的. With in-class initialisation, you don't need a default constructor either.使用类内初始化,您也不需要默认构造函数。 And finally, you don't need to write a destructor at all, virtual or not.最后,您根本不需要编写析构函数,无论是否虚拟。

The paper linked shows the relevant code:链接的论文显示了相关代码:

std::unique_ptr<Derived> { new Derived };

The stored deleter is std::default_delete<Derived> , which doesn't require Base::~Base to be virtual.存储的删除器是std::default_delete<Derived> ,它不需要Base::~Base是虚拟的。

Now you can move this to a unique_ptr<Base> , and it will also move the std::default_delete<Derived> without converting it to a std::default_delete<Base> . 现在您可以 将其移动unique_ptr<Base> ,它还会移动 std::default_delete<Derived>而不将其转换为 std::default_delete<Base>

To answer the specific question...要回答具体问题...

But is it useless now in C++11 to declare virtual your destructor if you have virtual members?但是,如果您有虚拟成员,现在在 C++11 中声明 virtual 您的析构函数是没有用的吗?

The need for a virtual destructor has NOT changed in the C++11 core language.在 C++11 核心语言中,对虚拟析构函数的需求没有改变。 You must declare your destructor as virtual if you are deleting the derived object using the base pointer.如果使用基指针删除派生对象,则必须将析构函数声明为虚拟的。

The statement in the slide gives the impression that somehow C++11 has changed the behavior with respect to virtual destructor - which is not the case.幻灯片中的声明给人的印象是 C++11 以某种方式改变了与虚拟析构函数相关的行为——事实并非如此。 As the author has clarified, it's only applicable when using a shared_ptr .正如作者所澄清的那样,它仅适用于使用shared_ptr But the fact that a virtual destructor is still required (except with the use shared_ptr ) gets diluted in the long explanation.但是,仍然需要虚拟析构函数(除了使用shared_ptr )这一事实在冗长的解释中被淡化了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM