说我有以下内容:
class Foo {
public:
void destroy();
~Foo()
{
destroy();
}
};
class Bar : public Foo {
public:
~Bar() {}
};
如果我有一个Bar对象,当对象栏被删除时会调用Foo的析构函数吗?
由于
答案 0 :(得分:16)
是
但上述代码很危险;如果您未将~Foo()
声明为virtual
,则如果您通过{{1}在~Bar()
对象上调用delete
,则不会调用Bar
}}
答案 1 :(得分:5)
是的,如果您持有Bar
参考,这将有效。然而,这是一个非常脆弱的解决方案,因为它不相反(持有Foo&
或Foo*
指向Bar
)。为了使其工作Foo
,析构函数必须是虚拟的。否则它将执行非虚拟调度,并仅在Foo
上调用析构函数。
class Foo {
public:
void destroy();
virtual ~Foo()
{
destroy();
}
};
class Bar : public Foo {
public:
virtual ~Bar() {}
};
在预期继承的一般情况下,涉及的类型应使用虚拟析构函数进行标记。像大多数规则一样,尽管100%的情况并非如此。但是,不要在这里涉及所有案例,我鼓励您阅读以下文章中的问题#2(感谢Steve的链接)
答案 2 :(得分:2)
如果派生类可能包含任何重要内容,则使基类的析构函数为虚拟。事实上,如果有人从你的班级中获得任何机会,你应该把它变成虚拟的。
无论如何,如果你实现了析构函数,你还应该实现Big-3:
这里有一些代码可以使虚拟析构函数的行为变得明显:
#include <iostream>
class Base1
{
public:
~Base1() // Uh-oh, not virtual...
{
std::cout << "~Base1()\n";
}
};
class Derived1 : public Base1
{
public:
~Derived1()
{
std::cout << "~Derived1()\n";
}
};
class Base2
{
public:
virtual ~Base2() // Good, it's virtual...
{
std::cout << "~Base2()\n";
}
};
class Derived2 : public Base2
{
public:
~Derived2()
{
std::cout << "~Derived2()\n";
}
};
int main()
{
std::cout << "Simple Base1: ";
Base1* b1 = new Base1();
delete b1;
std::cout << "Simple Base2: ";
Base2* b2 = new Base2();
delete b2;
std::cout << "Simple Derived1: ";
Derived1* d1 = new Derived1();
delete d1;
std::cout << "Simple Derived2: ";
Derived2* d2 = new Derived2();
delete d2;
std::cout << "Polymorphic Derived1: ";
b1 = new Derived1();
delete b1;
std::cout << "Polymorphic Derived2: ";
b2 = new Derived2();
delete b2;
return 0;
}
简单Base1:~Base1()
简单Base2:~Base2()
Simple Derived1:~Derived1()
〜基础1()
Simple Derived2:~Derived2()
〜和Base2()
多态Derived1:~Base1()注意 - 我在另一个答案中读到这个行为是未定义的,但这也差一点,如果不是更糟的话。基本上,确保您的基类具有虚拟析构函数
多态Derived2:~Derived2()
〜和Base2()
答案 3 :(得分:0)
我在这里会有点迂腐并说“是的,但有时候答案是否定的”。例如:
class Foo {
public:
void destroy();
~Foo()
{
destroy();
}
};
class Bar : public Foo {
public:
~Bar() { exit (0); } // code never returns from exit call so base class
// destructor never executed!
};
抛出异常也可能会中断正常的析构函数序列。此外,析构函数中的任何未定义行为都将消除调用基类析构函数的保证(因为任何事情都可能发生)。
但通常,在派生类析构函数完成后调用基类析构函数。
答案 4 :(得分:-5)
您需要在基类中声明析构函数“virtual”以获得该行为。