在下面的代码中,当删除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
的内存何时以及如何解除分配或回收?
答案 0 :(得分:8)
如果基类中的析构函数不是delete
,则在指向派生类对象的基类指针上调用virtual
是未定义的行为。
未定义的行为意味着任何事情都可能发生。无论内存是否泄漏都无关紧要,事实是您的程序可以显示任何行为,并且您不能依赖于显示的任何行为。
为使您的代码有效,Base类中的析构函数必须为virtual
。
C ++ 11标准5.3.5删除:
第3段:
在第一个备选(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类静态类型应具有虚拟析构函数或行为未定义。在第二种方法(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的。
答案 1 :(得分:2)
我认为调用不正确的析构函数是未定义的行为。你应该有一个虚拟的析构函数。
但是,由于分配在Derived类的一个块中完成,因此它“有效”,因为delete的内部只是跟踪分配的大小,从而释放了对象占用的8个字节, Base将使用的四个。
请注意,虚拟析构函数本身不负责释放this
指向的内存。在调用析构函数后,delete
中会发生这种情况。
答案 2 :(得分:1)
正如其他伟人提到的那样,标准表明它是未定义的行为,但通常是关注竞争和用户的编译器这取决于系统分配器实现,这可能因平台而异。析构函数本身不会释放包含字段的Derived
对象的内存。
当您调用new Derived
时,将调用operator new
的全局实现,并接收要分配的字节数。在您的情况下,它将是sizeof(Derived)
。
实际内存释放(全局operator delete
)后,分配器将知道从被释放对象的地址中释放多少字节。因此,b
和d
使用的内存将被回收