在 C++ 继承中,当指向基类的指针对象指向派生类时,不调用派生类析构函数

Posted

技术标签:

【中文标题】在 C++ 继承中,当指向基类的指针对象指向派生类时,不调用派生类析构函数【英文标题】:In C++ Inheritance, Derived class destructor not called when pointer object to base class is pointed to derived class 【发布时间】:2014-08-09 15:41:26 【问题描述】:

我是新手,我知道这是一个非常基本的概念,也可能是重复的。 构造函数一旦被调用,它对应的析构函数就必须被调用,这不是真的吗? [代码在 Dev C++ 上运行]

class Base
    
     public:
            Base()  cout<<"Base Constructor\n";
            int b;
     ~Base() cout << "Base Destructor\n"; 
    ;

class Derived:public Base

 public:
        Derived()  cout<<"Derived Constructor\n";
        int a;
 ~Derived()  cout<< "Derived Destructor\n"; 
; 
int main () 
Base* b = new Derived;    
//Derived *b = new Derived;
 delete b;
    getch();
    return 0;

给出输出

Base Constructor
Derived Constructor
Base Destructor

【问题讨论】:

不确定是否是问题所在,但您是否使用虚拟析构函数进行了测试? 您通常希望将基类的析构函数声明为virtual 方法,否则当您使用Base * 指针删除Derived 对象的实例时,将不会调用~Derived() 【参考方案1】:

您的代码具有未定义的行为。基类的析构函数必须是 virtual 以使以下行为具有定义的行为。

Base* b = new Derived;    
delete b;

来自 C++ 标准:

5.3.5 删除

3 在第一种选择(删除对象)中,如果 操作数与其动态类型不同,静态类型应为操作数动态类型的基类,且静态类型应具有虚析构函数或行为未定义。

所以在你的例子中,静态类型是Base,动态类型是Derived。所以Base的析构函数应该是:

virtual ~Base() cout << "Base Destructor\n"; 

【讨论】:

这里有一个很好的在线参考,详细说明了这种行为:en.cppreference.com/w/cpp/language/delete。以下是相关部分:“如果表达式计算为指向使用 new 分配的对象的基类子对象的指针,则基类的析构函数必须是虚拟的,否则行为未定义。” 【参考方案2】:

如果是微不足道的,就不需要调用析构函数。

这对您的示例没有帮助,因为如果一个类有一个带有非平凡析构函数(成员或基类)的子对象,或者它自己的析构函数是用户定义的,这不是微不足道的。

此外,如果该基类具有virtual 析构函数,则您只能使用指向基类的指针删除类,这将带来undefined behavior 的痛苦(编译器无需警告您)。

专业提示:如果您需要将其变为虚拟(例如由于上述要求),但不想阻止它变得琐碎(某些容器和算法已针对琐碎类型优化实现),请使用:

virtual ~MyClass = default; // Since C++11

因此,如果 Base 没有虚拟析构函数或者它实际上是一个基数,则永远不要使用指向 Base 的指针进行删除。

【讨论】:

如果析构函数是微不足道的,为什么它需要是虚拟的? 如前所述,因为您可能想使用Base* 删除Derived 但是如果析构函数是微不足道的,那么Derived中的什么需要删除呢? @NeilKirk:delete 表达式不仅需要调用正确的析构函数,还需要将正确的指针和大小传递给正确的操作符delete。无论如何,这就是为什么如果基址没有虚拟析构函数则通过基址指针删除是 UB 的原因。

以上是关于在 C++ 继承中,当指向基类的指针对象指向派生类时,不调用派生类析构函数的主要内容,如果未能解决你的问题,请参考以下文章

如果继承类型受到保护,我可以使基类的指针指向派生对象吗? [复制]

在 C++ 中将派生类对象分配和访问到基类“指向指针”对象

C++继承,发送一个指向基类的指针

在 C++ 中处理类?

指向派生对象的基类指针的 C++ 排序容器

在 C++ 中将指向基类的指针传递给派生类的成员函数