为什么此代码会导致内存泄漏? 它说如果基类析构函数是虚拟的,那么内存泄漏问题就会得到解决。为什么?
class base {
public:
base () { ptr = new int[1024]; }
~base () { delete [] ptr; }
private:
double *ptr;
};
class der : public base {
public:
der () { name = new char[1024]; }
~der () { delete [] name; }
private:
char *name;
};
int main () {
base* aPointer = new derived;
delete aPointer;
return 0;
}
答案 0 :(得分:2)
类Base
和Derived
都没有虚拟析构函数,这意味着析构函数不在vtable中,所以当你写:
Base * base = new Derived();
...
delete base;
通常情况下,只会调用Base::~Base()
,这意味着不会调用Derived::~Derived()
,这反过来意味着如果您期望Derived::~Derived()
到"delete base;"
,则会出现内存泄漏被称为。
此问题的解决方案是使析构函数虚拟,以便将其放入vtable中。
因此Derived
将调用动态类型({{1}})的析构函数并正确销毁对象。
答案 1 :(得分:1)
它泄漏,这是未定义的行为。
如果你的派生类没有引入额外的堆分配变量,那么它可能不会,但仍然是未定义的行为,所以猜测是没有意义的。
除非base
具有虚拟析构函数,否则使用base*
删除派生对象将无法为其他派生类及其成员和其他基础调用析构函数...此处name
不会被解除分配。
据说如果基类析构函数变为“虚拟”,则会解决内存泄漏问题。为什么?
virtual
函数是编译器从仅具有指针或对基类的引用的代码协调对派生类特定函数的调用的方式。这是C ++中面向对象编程支持的一个基本方面,如果你不理解它,你应该得到一本好书或在线教程,但是要给出一个非常简洁的介绍:虚函数有点像有一个函数指针在通常指向基类的函数实现的基类中,但是如果派生的类构造函数运行,那么函数指针将被其自己的实现的地址覆盖(实际上,虚拟调度往往使用特定于类的函数指针表)更好的内存效率,但可观察的功能类似于我所描述的)。因此,当delete
使用静态类型为base*
的指针时,派生的析构函数将不会运行,除非它是virtual
- 没有由派生类型协调的类似函数指针的间接可以安排这个。
答案 2 :(得分:0)
删除对象时,C ++可以通过两种方式决定要调用的析构函数。
如果指向该对象的指针已声明为T*
类型,但T::~T()
是虚函数,则C ++在虚函数表中查找以查找要调用的析构函数。通过存储在该对象实例的存储器内的指针找到虚函数表,该指针由实际构造的类确定,而不是由指针的类型确定。对于由base* aPointer = new der
初始化的对象,如果已将~base()
声明为虚拟,则C ++将能够查看实际具体类的虚函数表(der
,而不是base
}),当它这样做时,它会找到析构函数~der()
。这将删除name
,然后调用类der
定义的析构函数派生自;也就是说,它会调用~base()
,删除ptr
。如果base
是从其他类派生的,那么它的析构函数会调用该类的析构函数,依此类推,直到达到一个非派生类的类。
但是如果指向对象的指针已声明为T*
类型,并且T
的析构函数不是虚拟的,则编译器可以使用的唯一析构函数是已定义的析构函数当您定义T
时,即T::~T()
。因此,由于aPointer
被声明为base*
,delete aPointer
将首先调用~base()
,删除ptr
,然后返回,因为没有可以调用其析构函数的其他基类。在这种情况下永远不会调用析构函数~der()
,因此不会删除name
。那是内存泄漏。