C++基类中虚析构函数(c++虚基类和虚函数的区别)
虚析构函数
C++中基类采用virtual虚析构函数是为了防止内存泄漏。假设派生类中申请了内存空间,需要在析构函数中释放内存;若基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。
下面是基类和派生类中都没有虚析构函数的一个实例:
#include <iostream>
using namespace std;
class Base
{
public:
Base() //Base的构造函数
{
name = new char[100];
cout << "Base constructor" << endl;
}
~Base() //Base的析构函数
{
delete name;
name = nullptr;
cout << "Base destructor" << endl;
}
virtual void print()
{
cout << "Do something in class Base!" << endl;
};
private:
char* name;
};
class Derived : public Base
{
public:
Derived() //Derived的构造函数
{
name = new char[100];
cout << "Derived constructor" << endl;
}
~Derived() //Derived的析构函数
{
delete name;
name = nullptr;
cout << "Derived destructor" << endl;
}
void print()
{
cout << "Do something in class Derived!" << endl;
};
private:
char* name;
};
int main()
{
Derived* Test1 = new Derived(); //Derived类的指针
Test1->print();
delete Test1;
cout << endl;
Base* Test2 = new Derived(); //Base类的指针
Test2->print();
delete Test2;
return 0;
}
运行结果:
从运行结果可以看出,当通过基类指针删除派生类对象时,派生类的析构函数根本没有调用,name指针创建之后没有删除,导致内存泄漏。原因时派生类重写了基类析构函数,虽然派生类和基类的析构函数名字不同,但是编译器对析构函数做了特殊处理。在内部,派生类和基类的析构函数名字是一样的。所以当基类的析构函数为非虚函数时,就不能构成多态,基类的析构函数隐藏了派生类的析构函数,所以只能调用基类的析构函数。
下面是基类定义虚析构函数的一个实例:
#include <iostream>
using namespace std;
class Base
{
public:
Base() //Base的构造函数
{
name = new char[100];
cout << "Base constructor" << endl;
}
virtual ~Base() //Base的析构函数
{
delete name;
name = nullptr;
cout << "Base destructor" << endl;
}
virtual void print()
{
cout << "Do something in class Base!" << endl;
};
private:
char* name;
};
class Derived : public Base
{
public:
Derived() //Derived的构造函数
{
name = new char[100];
cout << "Derived constructor" << endl;
}
~Derived() //Derived的析构函数
{
delete name;
name = nullptr;
cout << "Derived destructor" << endl;
}
void print()
{
cout << "Do something in class Derived!" << endl;
};
private:
char* name;
};
int main()
{
Derived* Test1 = new Derived(); //Derived类的指针
Test1->print();
delete Test1;
cout << endl;
Base* Test2 = new Derived(); //Base类的指针
Test2->print();
delete Test2;
return 0;
}
运行结果:
从运行结果可以看出,定义了虚析构函数后,用一个基类的指针删除一个派生类的对象时,派生类的析构函数也被调用了。原因是当基类析构函数定义为虚函数后,删除对象时会直接调用派生类的析构函数,由于派生类析构时会先调用基类的析构函数,所以就把派生类和继承的基类都析构了。
总结:
- 基类的析构函数不加virtual关键字:当基类的析构函数不声明成虚析构函数,派生类继承基类,基类的指针指向派生类时,delete删除基类的指针,只调动基类的析构函数,而不调动派生类的析构函数。
- 基类的析构函数加virtual关键字:当基类的析构函数声明成虚析构函数,派生类继承基类,基类的指针指向派生类时,delete删除基类的指针,先调动派生类的析构函数,再调动基类的析构函数。
- 由于基类的析构函数为虚函数,所以派生类会在所有属性的前面形成虚表,而虚表内部存储的就是基类的虚函数。
- 当delete基类的指针时,由于派生类的析构函数与基类的析构函数构成多态,所以得先调动派生类的析构函数;之所以再调动基类的析构函数,是因为delete的机制所引起的,delete 基类指针所指的空间,要调用基类的析构函数。