虚析构函数
虚函数(virtual)用在类中的普通方法中的时候,会根据虚表(vtable)查找派生类中对于当前函数名的用法,虚函数可以用于实现多态,而实现多态的时候(父类引用或者指针接收子类对象),在删除该父类指针时不会调用子类的析构函数,而如果子类对象中具有开辟在堆区上的内存空间,就不能通过子类析构函数删除该部分空间,造成内存泄漏,此时就用到了虚析构函数,与普通虚函数不同,它并非直接查找子类中的函数覆盖自身,而是同时调用自身析构函数与子类构造函数,这样就解决了利用多态时可能造成内存泄漏的问题。
注意:构造函数不可以被设定为虚函数:虚函数对应一个虚表vtable,这个vtable存储在对象的内存空间中,虚函数被通过查询虚表vtable调用,所以一定要通过构造函数实例化内存空间,才会有虚表,所以构造函数不可能为虚函数。
#include<iostream>
class Base
{
public:
Base() { std::cout << "Base Constructor\n"; }
~Base() { std::cout << "Base Destructor\n"; }
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived Constructor\n"; }
~Derived() { std::cout << "Derived Destructor\n"; }
};
int main()
{
Base* base = new Base();
delete base;
std::cout << "-----------------\n";
Derived* derived = new Derived();
delete derived;
std::cout << "-----------------\n";
Base* poly = new Derived();
delete poly;
std::cin.get();
}
运行发现父类构造函数和子类构造函数都能被正确调用,当删除poly时只有父类的析构函数被调用了,而子类的析构函数没有被调用,导致内存泄漏。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/879c21f9fb494b7e9598278d40932d13.png
原因:当删除poly时,它没有被标记为虚函数,是一个父类的指针,由于没有虚函数表(vtable),它只会调用自身析构函数。
现在对父类Base的虚函数加上virtual关键字变为虚函数,再在子类中加入一个开辟在堆区的数组
#include<iostream>
class Base
{
public:
Base() { std::cout << "Base Constructor\n"; }
virtual ~Base() { std::cout << "Base Destructor\n"; }
};
class Derived : public Base
{
public:
int* a;
Derived()
{
a = new int[5];
std::cout << "Derived Constructor\n";
}
~Derived()
{
delete[] a;
std::cout << "Derived Destructor\n";
}
};
int main()
{
Base* base = new Base();
delete base;
std::cout << "-----------------\n";
Derived* derived = new Derived();
delete derived;
std::cout << "-----------------\n";
Base* poly = new Derived();
delete poly;
std::cin.get();
}
析构函数被标记为virtual,意味着c++知道需要调用子类中的析构函数。在调用析构函数时再同时调用类析构函数。它会先调用派生类析构函数,然后在层次结构中向上调用基类析构函数。