在 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++ 继承中,当指向基类的指针对象指向派生类时,不调用派生类析构函数的主要内容,如果未能解决你的问题,请参考以下文章