编译器如何处理派生析构函数中的 base class 析构函数调用?
How does the compiler handle base class destructor calls in the derived destructor?
出于好奇,我尝试做类似下面示例的操作,看看编译器是否给我警告,而不是调用一个最终导致堆栈溢出的无限循环。我想也许有一种不同于仅仅调用普通函数或方法的行为。但事实并非如此。对此是否有特殊解释,或者它只是作为普通函数调用处理,因为我通过使用 this
运算符明确调用基本 class 析构函数?
示例:
class A {
virtual ~A();
};
class B : A {
virtual ~B() { this->~A(); }
};
@M.M的评论中了。您正在两次调用析构函数。这是未定义的行为,任何事情都可能发生,包括您观察到的行为。
(实际上,很可能其中一个析构函数调用修改了对象的 vptr,这意味着后续的析构函数调用不再转到最派生的对象。但这只是一个猜测。)
正确的做法是不要手动调用析构函数。
调用派生的 class 虚拟析构函数导致调用基 class 析构函数。但反之则不然。
派生 class 中的虚拟析构函数总是首先调用父 class 析构函数,递归顺序使得最 "ancestral" base-class' 析构函数将被调用,然后是第二大-"ancestral",等等。想象一下,Child继承自Parent,Parent继承自GrandParent。 Child class' 的析构函数实际上会调用 GrandParent 的析构函数,然后是 Parent 的析构函数,然后是 Child 的析构函数。
事实上,您派生的 class 构造函数也以相同的递归顺序调用它们的父 class 构造函数。您必须将派生的 class 想象成 "layer cake":每个继承实例都会为您的对象添加一层。所以Childclass有3层{GrandParent,Parent,Child},每一层的construction/destruction由对应的class.
处理
您正在尝试的将尝试调用父析构函数两次,这是个坏主意。通常您不必显式调用析构函数,除非您重载了 new
运算符。有关详细信息,请参阅此答案:Is calling destructor manually always a sign of bad design?
出于好奇,我尝试做类似下面示例的操作,看看编译器是否给我警告,而不是调用一个最终导致堆栈溢出的无限循环。我想也许有一种不同于仅仅调用普通函数或方法的行为。但事实并非如此。对此是否有特殊解释,或者它只是作为普通函数调用处理,因为我通过使用 this
运算符明确调用基本 class 析构函数?
示例:
class A {
virtual ~A();
};
class B : A {
virtual ~B() { this->~A(); }
};
@M.M的评论中了。您正在两次调用析构函数。这是未定义的行为,任何事情都可能发生,包括您观察到的行为。
(实际上,很可能其中一个析构函数调用修改了对象的 vptr,这意味着后续的析构函数调用不再转到最派生的对象。但这只是一个猜测。)
正确的做法是不要手动调用析构函数。
调用派生的 class 虚拟析构函数导致调用基 class 析构函数。但反之则不然。
派生 class 中的虚拟析构函数总是首先调用父 class 析构函数,递归顺序使得最 "ancestral" base-class' 析构函数将被调用,然后是第二大-"ancestral",等等。想象一下,Child继承自Parent,Parent继承自GrandParent。 Child class' 的析构函数实际上会调用 GrandParent 的析构函数,然后是 Parent 的析构函数,然后是 Child 的析构函数。
事实上,您派生的 class 构造函数也以相同的递归顺序调用它们的父 class 构造函数。您必须将派生的 class 想象成 "layer cake":每个继承实例都会为您的对象添加一层。所以Childclass有3层{GrandParent,Parent,Child},每一层的construction/destruction由对应的class.
处理您正在尝试的将尝试调用父析构函数两次,这是个坏主意。通常您不必显式调用析构函数,除非您重载了 new
运算符。有关详细信息,请参阅此答案:Is calling destructor manually always a sign of bad design?