虚析构函数在多态时是非常有用的。
当通过基类指针释放子类对象时,子类的析构函数并不会调用。这可能会导致内存泄漏问题。
例如:
class Base
{
public:
Base(){ std::cout << "Base constructor" << std::endl;}
~Base(){ std::cout << "Base destructor" << std::endl;}
};
class Derived : public Base
{
public:
Derived(){ m_Array = new int[10]; std::cout << "Derived constructor" << std::endl;}
~Derived(){ delete[] m_Array; std::cout << "Derived destructor" << std::endl;}
private:
int* m_Array;
};
int main(){
Base* base = new Base();
delete base;
std::cout << "-------------------" << std::endl;
Derived* derived = new Derived();
delete derived;
std::cout << "-------------------" << std::endl;
Base* poly = new Derived();
delete poly;
}
输出如下:
Base constructor
Base destructor
-------------------
Base constructor
Derived constructor
Derived destructor
Base destructor
-------------------
Base constructor
Derived constructor
Base destructor
可以发现m_Array在上述情况中并没有被释放,解决的办法是声明基类的析构为virtual。但是这里区别是,子类的析构函数并不会替换掉基类的析构函数,而是增加。
virtual ~Base(){ std::cout << "Base destructor" << std::endl;}
输出的结果为
Base constructor
Derived constructor
Derived destructor
Base destructor
关于内存泄漏
内存泄漏不光是没有释放这种情况,还可能是错误释放。
比如一个二维数组
int main(){
int** a2d = new int*[5];
for (int i = 0; i < 5; i++)
{
a2d[i] = new int[5];
}
delete[] a2d;
}
如果如上直接释放a2d,那么释放的只有这个指针数组,而指针数组中每个指针保存的真正数组则无法再被访问到,进而无法释放。因此正确的方式是,先循环释放所有指针的内容再释放指针数组。
for (int i = 0; i < 5; i++)
{
delete[] a2d[i];
}
delete[] a2d;
对于这个内存计算,32位和64位的程序,指针占用的空间不一样大。以32位为例,int是4字节,指针时32位也是4字节,那么上述a2d的二维数组总共占用45+45=40字节,而如果是上述开始的错误释放,那么只能释放指针数组所占用的内存4*5=20字节(int** a2d),而5个整型数组则没有被释放。