我理解派生类的类型与指向其基类的指针兼容。在给定的示例代码中,new bar
对象构造发生在调用foo::foo()
后跟bar::bar()
。在各自的构造函数中,我将资源分配给类成员foo::int *a
和bar::int *b
。
现在我将这样构造的对象初始化为基类类型。使用obj
,我可以调用基类析构函数,但不能调用派生类析构函数。那么,在这种情况下如何释放派生类资源呢?这不是内存泄漏吗?
#include <iostream>
class foo
{
int *a;
public:
foo()
{
a = new int[5];
std::cout << "\n foo constructor" << std::endl;
}
~foo()
{
std::cout << "\n foo destructor" << std::endl;
delete[] a;
}
};
class bar : public foo
{
int *b;
public:
bar()
{
b = new int[5];
std::cout << "\n bar constructor" << std::endl;
}
~bar()
{
std::cout << "\n bar destructor" << std::endl;
delete[] b;
}
};
int main()
{
foo *obj = new bar; // Derived class object is type compatible with base class
delete obj; // Equivalent to obj->~foo();
return 0;
}
感谢。
答案 0 :(得分:4)
这就是“虚拟析构函数”的概念所在。从技术上讲,如果通过基类类型的指针删除对象,则必须将该基类析构函数标记为虚拟或结果未定义。如果您将析构函数标记为虚拟,则其含义与其他虚函数不同。它意味着“当通过基类指针删除此对象时,在调用基础构造函数之前调用派生的析构函数”,而不是意味着“派生类重写此行为”。这是你想要的行为。
作为一般规则,您在子类化上计划的任何类都应该有一个虚拟析构函数来防止出现这种问题。
答案 1 :(得分:3)
如果delete
通过指向其某个基类的指针派生类对象,则基类析构函数必须声明为virtual
,否则行为未定义。
如果~foo()
被宣布为virtual
,那么你很高兴。首先会调用~bar()
,然后调用~foo()
。
答案 2 :(得分:3)
这实际上是一种未定义的行为。
来自标准文档。 5.3.5.3删除,
在第一个备选(删除对象)中,如果操作数的静态类型与其动态类型不同,静态类型 应该是操作数的动态类型的基类,而静态类型应该具有虚拟析构函数或者行为是 未定义。 ......
答案 3 :(得分:0)
这样做,
virtual ~foo()
{
//your code
}
这确保了执行delete *pFoo
也会调用派生类析构函数(~bar()
)。调用顺序为~bar()
,后跟~foo()
。
如果您对~bar()
执行相同操作(即
virtual ~bar()
{
//your code
}
虽然如果您不想从bar
进一步派生并希望将bar*
用于派生类,那么这种情况并不是那么多。