3

我读到虚拟析构函数必须在具有虚拟方法的类中声明。我只是不明白为什么必须将它们声明为虚拟的。我知道为什么我们需要虚拟析构函数,如下例所示。我只是想知道为什么编译器不为我们管理虚拟析构函数。关于虚拟析构函数的工作,我需要了解些什么吗?下面的例子表明,如果析构函数没有声明为虚拟的,那么派生类的析构函数不会被调用,为什么会这样?

class Base 
{
    // some virtual methods
public:
    Base()
    {std::cout << "Base Constructor\n";}
    ~Base()
    {std::cout << "Base De-structor\n";}

};

class Derived : public Base
{
public:
    Derived()
    {std::cout << "Der constructor\n";}
    ~Derived()
    { std::cout << "Der De-structor\n";}
} ;         
void main()
{

    Base *b = new Derived();
    delete b;
}
4

3 回答 3

4

我只是想知道为什么编译器不为我们管理虚拟析构函数。

因为在 C++ 中,您为使用的东西付费。默认情况下有一个virtual析构函数涉及编译器向类添加一个虚拟表指针,这会增加它的大小。这并不总是可取的。

下面的例子表明,如果析构函数没有声明为虚拟的,那么派生类的析构函数不会被调用,为什么会这样?

该示例表现出未定义的行为。这简直是​​违反规则的。并非所有析构函数都被调用这一事实只是一种可能的表现形式。它可能会崩溃。

关于虚拟析构函数的工作,我需要了解些什么吗?

是的。如果您通过指向基类的指针删除对象,则它们是必需的。否则它是未定义的行为。

5.3.5 删除[expr.delete]

3)在第一种选择(删除对象)中,如果待删除对象的静态类型与其动态类型不同,则静态类型应为待删除对象的动态类型和静态类型的基类应具有虚拟析构函数或行为未定义。在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。(强调我的)

于 2012-10-22T20:21:19.433 回答
3

我读到虚拟析构函数必须在具有虚拟方法的类中声明。

是的。但这是过于简单化了。
这并不是说具有虚拟方法的类需要虚拟析构函数。但是使用具有虚拟方法的类的方式意味着它通常需要一个虚拟析构函数。当您通过指向其基类的指针删除对象时才需要虚拟析构函数。问题在于,当一个对象具有虚拟方法时,您通常使用指向其基类的指针,即使实际对象略有不同。

我只是不明白为什么必须将它们声明为虚拟的。

这不是他们必须的。如上所述。这是通常使用模式的结果。

我只是想知道为什么编译器不为我们管理虚拟析构函数。

因为它并不总是需要。C++ 的精神是您不必为不需要的东西付费。如果编译器总是将虚拟析构函数添加到具有虚拟方法的类中,那么即使在我可以在我的代码库中证明我不需要它的情况下,我也必须为使用虚拟析构函数付出代价。

关于虚拟析构函数的工作,我需要了解些什么吗?

只是使用它们需要一点成本。

如果没有将析构函数声明为虚拟的,则不会调用派生类的析构函数,为什么会这样?

这就是为什么我们有虚拟析构函数来导致这种行为。如果您需要此行为,则需要添加虚拟析构函数。但是在某些情况下,可能不需要虚拟析构函数,这使得这种方法的用户无需付出代价。

于 2012-10-22T20:32:17.650 回答
3

我读到虚拟析构函数必须在具有虚拟方法的类中声明。

“必须”这个词太强了:“应该”更适合该建议。

我只是想知道为什么编译器不为我们管理虚拟析构函数。

C++ 设计者试图避免编译器只在最极端的情况下做你没有要求它做的事情。语言设计者认识到使类具有多态性的决定应该由程序的设计者决定,因此他们拒绝将这一责任重新分配给编译器。

下面的例子表明,如果析构函数没有被声明为虚拟的,那么派生类的析构函数不会被调用,为什么会这样呢?

因为您的代码无效:通过声明Derivednon-virtual 的析构函数,您承诺永远不会Derived通过指向Base;的指针进行销毁。你main违反了这个承诺,调用了未定义的行为。

请注意,仅b通过使用确切类型声明变量,您就可以避免与非虚拟析构函数相关的问题(指向 ideone 的链接)。但是,这会导致设计相当不稳定,因此您应该避免使用虚拟函数和非虚拟析构函数的继承层次结构。

于 2012-10-22T20:29:26.000 回答