6

在下面的代码中,当ptr被删除时,Base调用的是析构函数for,而不是析构函数for Derived(由于析构函数Base不是虚拟的)。

class Base
{
  int b;
};

class Derived : public Base
{
  int d;
};

int main(void)
{
    Base * ptr = new Derived();
    delete ptr;
    return 0;
}

Valgrind 报告说该程序不包含内存泄漏,我猜这是真的,因为在这种特殊情况下所有新数据都被删除了。我的问题是 - 鉴于Derived未调用(默认)析构函数,何时以及如何d释放或回收内存?

4

3 回答 3

8

delete如果基类中的析构函数不是,则调用指向派生类对象的基类指针是未定义的行为virtual

未定义的行为意味着任何事情都可能发生。内存泄漏与否无关紧要,事实是您的程序可以显示任何行为并且您不能依赖显示的任何行为。

要使您的代码有效,基类中的析构函数必须是virtual.


C++11 标准 5.3.5 删除:
第 3 段:

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

于 2013-02-22T11:34:32.460 回答
2

我认为调用不正确的析构函数是未定义的行为。你应该有一个虚拟析构函数。

然而,由于 Derived 类的分配是在一个块中完成的,它“起作用”是因为 delete 的内部只是简单地跟踪分配的大小,从而释放对象占用的 8 个字节,而不是 4 个该基地将使用。

请注意,虚拟析构函数本身不负责释放指向的内存this。这发生在delete调用析构函数之后。

于 2013-02-22T11:35:24.703 回答
1

正如其他伟人所提到的,标准说这是未定义的行为,但通常在关心竞争和用户的编译器中,这取决于系统分配器的实现,这可能因平台而异。析构函数本身不会释放Derived包含字段的对象的内存。

当您调用 时new Derived,将调用 的全局实现operator new并接收要分配的字节数。这将是sizeof(Derived)你的情况。

在实际内存释放(全局operator delete)时,分配器将知道要从被释放对象的地址中释放多少字节。b因此,两者使用的内存d将被回收

于 2013-02-22T11:27:54.633 回答