【问题标题】:Base class has no destructor, but derived class does. Do I need to look for any pitfalls that DON'T relate to the heap?基类没有析构函数,但派生类有。我是否需要寻找与堆无关的任何陷阱?
【发布时间】:2016-09-19 02:12:07
【问题描述】:

在继承方面,我知道建议您的类的析构函数是虚拟的,因此除了任何派生析构函数之外,基类的析构函数会被正确调用。但是,我想知道在以下场景中是否存在与派生对象相关的堆栈相关问题。

假设我们有一个没有析构函数的 Base 类(无论出于何种原因):

class Base{};

还有一个确实有析构函数的派生类:

class Derived : public Base
{
  ~Derived(){}
};

主要是...:

int main()
{
  Derived a;
  return 0;
}

我是否会遇到没有析构函数的基类的任何问题?我最初的猜测是编译器只会为 Base 类生成一个默认的析构函数。同样,我的问题主要与堆栈有关,而不是动态内存:是否有任何奇怪的情况我需要注意以避免调用 Derived 析构函数而 Base 析构函数不被调用?

【问题讨论】:

    标签: c++ inheritance destructor


    【解决方案1】:

    您的基类有一个隐式析构函数。一切都会好起来的。

    虚拟基类析构函数用于允许派生构造函数在通过指针或对基类的引用进行析构时运行。所以在你的情况下,这是不安全的:

    void destruct(Base &b) { b.~Base(); }
    Derived d; destruct(d);
    

    但这绝对安全:

    void destruct(Derived &d) { d.~Derived(); }
    Derived d; destruct(d);
    

    【讨论】:

    • 函数示例确实有助于说明这一点。非常感谢。
    • 这两个例子都不安全,因为d 将被破坏两次
    • @M.M 都不会编译,所以两者都是安全的。
    【解决方案2】:

    您正在考虑的规则是,如果您通过指向其基类型之一的指针删除派生类型的对象,并且该基类型没有虚拟析构函数,则行为未定义。此处的代码并没有删除任何内容,因此该规则不适用。

    【讨论】:

      【解决方案3】:

      为了确保安全,每个析构函数(隐式或显式)至少是以下之一就足够了:

      • virtual(基类需要通过基类指针删除子类实例)
      • protected(确保不可能通过基类指针尝试删除)
      • final(实际上是类的一个属性,避免子类的全部可能性)。

      有一些罕见的边缘情况可以安全地调用析构函数,但它们通常是设计不良的标志,如果你设法在其中一种情况下发生,很容易避免。

      顺便说一句,请注意 std::shared_ptr 类型擦除了它的删除器,因此即使 Base 没有公共析构函数,std::shared_ptr<Base> 也可以工作。

      【讨论】:

      • shared_ptr 在这方面真是太痛苦了。
      • @kakyo 你到底是什么意思?你不是用make_shared 什么的吗?
      • 我正在使用shared_ptr,希望可以避免调用基类析构函数。但我还没有找到办法。
      • Class B : public Class Aprotected: virtual A::~A() {}public: B::~B() override {// cleanup},然后是std::shared_ptr<ClassB> myvar = std::make_shared<ClassB>(); a_map.insert({id, myvar}),最后是a_map.erase(that_var)。我发现A::~A{} 仍然被调用。
      • 嗯,有趣,我用MSVC2017复制了它。
      猜你喜欢
      • 1970-01-01
      • 2014-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2017-12-18
      • 2011-10-11
      • 2017-07-18
      相关资源
      最近更新 更多