纯虚析构的问题
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
示例:
class Animal {
public:Animal(){cout << "Animal 构造函数调用!" << endl;}virtual void Speak() = 0;//析构函数加上virtual关键字,变成虚析构函数//virtual ~Animal()//{// cout << "Animal虚析构函数调用!" << endl;//}virtual ~Animal() = 0;
};Animal::~Animal()
{cout << "Animal 纯虚析构函数调用!" << endl;
}//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。class Cat : public Animal {
public:Cat(string name){cout << "Cat构造函数调用!" << endl;m_Name = new string(name);}virtual void Speak(){cout << *m_Name << "小猫在说话!" << endl;}~Cat(){cout << "Cat析构函数调用!" << endl;if (this->m_Name != NULL) {delete m_Name;m_Name = NULL;}}public:string *m_Name;
};void test01()
{Animal *animal = new Cat("Tom");animal->Speak();//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏//怎么解决?给基类增加一个虚析构函数//虚析构函数就是用来解决通过父类指针释放子类对象delete animal;
}int main() {test01();system("pause");return 0;
}
注意:即使是纯虚析构也需要有具体的实现。因为包含纯虚函数的类为抽象类,被继承后,在派生类析构函数被调用时抽象类析构函数也将被调用,因此必须有实现。唯一麻烦就是必须在类的定义之外(cpp文件)实现它。
总结:
1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3. 拥有纯虚析构函数的类也属于抽象类
纯虚函数的实现案例:纯虚析构函数必须有实现
纯虚函数可以有实现:唯一麻烦就是必须在类的定义之外(cpp文件)实现它。
申明一个函数为纯虚并不意味着它没有实现,它意味着:
- 当前类是抽象类 ;
- 任何从此类派生的实体类必须将此函数申明为一个“普通”的虚函数(也就是说,
不能带“= 0”)。
1、纯虚函数例子:
声明一个pure virtual
函数的目的是为了让 derived classes
只继承函数接口,派生类必须提供实现。
可以为pure virtual
函数提供实现,但使用时需要指明所属类,如:
//.h
class A{
public:virtual void func1() = 0;
};
class B : public A{
public:virtual void func1(){A::func1();};
};//.cpp
void A::func1(){..........}B b;
b.A::func1(); // 与b.func1()结果相同,相当于提供了缺省实现,但派生类需要主动指定。
2、纯虚析构函数必须有实现:
因为包含纯虚函数的类为抽象类,被继承后,在派生类析构函数被调用时抽象类析构函数也将被调用,因此必须有实现。
class A{
public:virtual ~A() = 0;
};//.cpp
A::~A(){...}
总结:
1、纯虚函数可以有实现,但必须在类的定义之外(cpp文件)实现。
2、纯虚析构函数必须有实现。
参考链接:
纯虚函数的实现案例:纯虚析构函数必须有实现