基类和派生类虚析构函数的影响

6

我对多个虚拟析构函数产生了一些疑虑,尤其是在阅读了http://blogs.msdn.com/b/oldnewthing/archive/2004/05/07/127826.aspx之后。假设我有:

class Base
{
    public:
        Base();
        virtual ~Base();

    private:
        Logger* _logger;
};

//and

class Derived : public Base{
    public:
        Derived();
        virtual ~Derived();

    private:
        Logger* _logger;
};

在 CPP 文件中,每个析构函数都会删除相应的 _logger 指针。
Base::~Base(){  //base.cpp
    delete _logger;
}
Derived::~Derived(){ //derived.cpp
    delete _logger;
}

这个是否能按我的意图成功运行,而且没有内存泄漏?


只要您将基础记录器保持为私有,那么这应该可以正常工作。 - pjwilliams
在所示的代码中,两个 _logger 指针都将具有不确定的值,因为它们没有被正确初始化。 - sehe
@sehe - 两个_logger变量在各自的.cpp文件中被初始化。 - icy
@icy 嗯,你说得完全正确,我不知道我怎么会忽略了那种可能性 :_ - sehe
2个回答

5
首先,如果您将基类析构函数设置为virtual,则所有派生类都将自动获得virtual析构函数,无论您是否将它们声明为virtual。对于匹配的签名来说,这通常是正确的:如果基类具有与派生类中的函数相同签名的虚函数,则派生类中的函数是一个override且是virtual(尽管在C++ 2011中,您可以使用final关键字防止进一步覆盖,在这种情况下,另一个覆盖会创建错误)。
话虽如此,析构函数是特殊的:当您将析构函数设置为virtual时,即使存在另一个覆盖析构函数,它仍将被调用!析构函数设置为virtual的唯一影响是,如果您使用指向基类的指针删除对象,而该对象实际上是派生类型,则会发生什么:如果析构函数不是virtual,则会出现未定义的行为,而如果析构函数是virtual,则会发生正确的事情。例如:
class not_a_base {};
class bad_idea: public not_a_base {};

class a_base { public: virtual ~a_base() {} };
class ok: public a_base {};

int main() {
    a_base* ab = new ok;
    delete ab; // <---- all is good here!

    not_a_base* nab = new bad_idea;
    delete nab; // <---- results in undefined behavior
}

析构函数默认不是虚函数的原因很简单,因为这意味着对象的大小总是增加一个字长,这在一般情况下是不可接受的。

感谢您澄清问题可能发生的确切时间。 - icy

4

Base::_loggerDerived::_logger是不同的变量。因此,您应该在Derived的析构函数中删除Derived::_logger,否则会泄漏内存。

请注意,这与它是私有的无关。考虑以下示例程序:

#include <iostream>

class A {
public:
    bool foo;
};

class B: public A {
public:
    bool foo;
};

int main()
{
    B b;
    std::cout << &b.B::foo << ' ' << &b.A::foo << '\n';
}

地址不同,这意味着它们是不同的变量,尽管它们具有相同的名称。由于每个类都引入了自己的命名空间,因此名称实际上不会冲突。第一个是 A::foo,另一个是 B::foo。 由于你的析构函数是虚拟的,因此两者都将被调用,并且在两者中都将删除正确的 _logger。

由于你的析构函数是虚函数,两个都会被调用,并且在它们中正确的_logger将被删除。谢谢确认 =) - icy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接