【问题标题】:Destruction of class members when destructor is not called不调用析构函数时销毁类成员
【发布时间】:2013-02-22 11:20:37
【问题描述】:

在下面的代码中,当ptr被删除时,Base的析构函数被调用,而不是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 的内存何时以及如何被释放或回收?

【问题讨论】:

  • 我相信,由于delete ptr,内存被回收了,只是没有调用析构函数,这可能是一个潜在未定义的行为。
  • 没有潜力。这是未定义的行为。 (§5.3.5/3)
  • 你的 valgrind 调用说“没有内存泄漏”,我的 MemoryLoggerallocated 8 bytes at 0x3b2380; deallocated 4 bytes at 0x3b2380; Error: 4 bytes still allocated!。 UB。可能这也可以说是Have a nice day sir
  • @iammilind:这是潜在的泄漏,因为Derived 可能负责一些因此不会被释放的更多资源。

标签: c++


【解决方案1】:

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

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

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


C++11 Standard 5.3.5 删除:
第 3 段:

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

【讨论】:

    【解决方案2】:

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

    但是,由于 Derived 类的分配是在一个块中完成的,因此它“有效”是因为 delete 的内部只是简单地跟踪分配的大小,从而释放对象占用的 8 个字节,而不是Base 将使用的四个中的一个。

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

    【讨论】:

    • 这种特定的删除实现很可能会跟踪分配的大小,但这不是必需的,而且它似乎工作的事实可能只是巧合。特别是,内存管理器通常具有它们将分配的最小块大小,并且分配给Derived 对象的块可能与分配给Base 对象的块大小相同。无论如何,未定义的行为只是未定义的;猜测为什么在特定情况下可能没有出现症状通常只是猜测。
    • 哦,当我谈到这个主题时,释放内存通常是在析构函数中完成的;它得到一个秘密标志,告诉它是否也删除内存。这使得处理特定于类的operator delete 和不同大小的派生类型变得更容易。对不起讲座...
    【解决方案3】:

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

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

    在实际内存释放时(全局operator delete),分配器将知道要从被释放对象的地址释放多少字节。所以bd使用的内存都会被回收

    【讨论】:

    • 不,你不能指望内存被释放。 cmets 是正确的,这是未定义的行为。你无法预测那时会发生什么。
    • @MSalters 是的,你是对的。我描述了它过去是如何为我工作的 - 不知道这只是一种方便..
    • @MSalters:Roman 只是试图解释这个特殊编译器在这种特殊情况下最可能发生的事情,以及为什么 valgrind 没有检测到任何内存泄漏。他没有说这不是UB。
    • @AndyT:他说“使用的内存......将被回收”。描述特定情况下的最终行为必然意味着不存在 UB。 (并且在 UB 的情况下描述 可能的 行为是毫无意义的,因为那时一切皆有可能)
    • 对。如果没有遗漏通常这个重要词,答案会很好。 通常它与描述的完全一样,但各种事情可能会干扰。特定于类的分配器肯定会。
    猜你喜欢
    • 2016-11-10
    • 2018-05-02
    • 1970-01-01
    • 2018-01-02
    • 2019-09-12
    • 1970-01-01
    • 2011-05-24
    • 2016-01-29
    • 1970-01-01
    相关资源
    最近更新 更多