【问题标题】:Why isn't the derived class destructor being called?为什么不调用派生类析构函数?
【发布时间】:2022-07-01 00:33:30
【问题描述】:

我正在使用指向派生类的指针进行一些练习,当我运行下面提供的代码时,我得到的输出是

Constructor A
Constructor B
Destructor A

有人能告诉我为什么 B::~B() 没有在这里被调用吗?

class A {
 public:
  A() { std::cout << "Constructor A\n"; }
  ~A() { std::cout << "Destructor A\n"; }
};

class B : public A {
 public:
  B() { std::cout << "Constructor B\n"; }
  ~B() { std::cout << "Destructor B\n"; }
};

int main() {
  A* a = new B;
  delete a;
}

【问题讨论】:

  • 如果函数不是virtual,则不能通过多态调用。 BA 的析构函数不是虚拟的
  • 因为您在A* 上调用delete,而不是B*,并且A* 的析构函数不是virtual。结果,程序具有未定义的行为,因为对象的最衍生类型实际上是B,而不是A

标签: c++ class inheritance declaration destructor


【解决方案1】:

指针a的静态类型为A *

A* a = new B;

所以在A类中搜索所有使用这个指针调用的成员函数。

要调用动态类型指针的析构函数,即类B,你需要在类A中将析构函数声明为virtual。例如:

#include <iostream>

class A {
 public:
  A() { std::cout << "Constructor A\n"; }
  virtual ~A() { std::cout << "Destructor A\n"; }
};

class B : public A {
 public:
  B() { std::cout << "Constructor B\n"; }
  ~B() override { std::cout << "Destructor B\n"; }
};

int main() {
  A* a = new B;
  delete a;
}

【讨论】:

    【解决方案2】:

    因为被覆盖的方法需要是虚拟的

    class A{
    public:
        A()
        {
            std::cout<<"Constructor A\n";
        }
        virtual ~A()
        {
            std::cout<<"Destructor A\n";
        }
    };
    class B : public A{
    public:
        B()
        {
            std::cout<<"Constructor B\n";
        }
        virtual ~B()
        {
            std::cout<<"Destructor B\n";
        }
    };
    

    【讨论】:

    • 第二个virtual是可选的,但override可以用来防止出错。
    • 更具体地说,任何时候您编写一个类的目的是为了将其子类化并在虚拟上下文中使用(即通过对基类的指针/引用),析构函数至少需要是虚拟的,即使它是virtual ~A() = default。否则,当实例通过具有父类型的变量获取deleted 时,您会遇到问题。
    • @SilvioMayolo ... 但是如果 deleter 与对象一起存储,您可以使用没有virtual析构函数的多态性,因为它是在std::shared_ptr (example) 中。
    • 所以当我“删除一个;”发生的事情是我调用 A::~A() 因为指针 a 不知道 B::~B() 存在,如果我覆盖 A::~A() 我让它搜索最派生的析构函数的版本,它运行到 B::~B() 并在它完成后它的对象 A::~A() 被调用?
    • @YourFBIAgent 是的,派生类的神奇魔力只适用于虚拟方法,其他明智的 c++ 只是说“他告诉我这是一个 A 对象,所以我会打电话给 A::~
    【解决方案3】:

    我最近遇到了类似的问题。你现在只触发了基类构造函数,所以你必须让它停止忽略派生类构造函数。

    这是一个单词修复:在你的析构函数前面添加“虚拟”。

    请注意,如果您使用的是类头文件和实现文件结构,根据我的经验,您只需在头文件中的定义前面放置“虚拟”即可。

    希望这会有所帮助!

    【讨论】:

    • 这不会增加现有答案中尚未发现的任何新内容。为什么要重复现有的答案?
    猜你喜欢
    • 2015-07-18
    • 1970-01-01
    • 2013-01-28
    • 2020-07-02
    • 2013-03-30
    • 2014-01-15
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多