本博文以及这个系列的都是我本人观看《Effective C++》的观后感,每天学习一个改善自己C++的一个小技巧。
条款07:为多态基类声明为virtual析构函数
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,给父类的虚构函数加上关键字virtual。
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;
};
class Dog : public Animal {
public:
Dog(string name)
{
cout << "Dog构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout << *m_Name << "小狗在说话!" << endl;
}
~Dog()
{
cout << "Dog析构函数调用!" << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
public:
string* m_Name;
};
void test01()
{
Animal* animal1 = new Cat("Tom");
animal1->Speak();//调用cat的speak函数
//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
//怎么解决?给基类增加一个虚析构函数
//虚析构函数就是用来解决通过父类指针释放子类对象
delete animal1;
cout << endl;
Animal* animal2 = new Dog("Ben");
animal2->Speak();//调用dog的speak函数
delete animal2;
}
运行时的效果
- C++的虚函数会产生运行时多态,达到晚绑定的的效果,所以很多C++设计模式都是围绕virtual的。
- 声明为虚函数都携带一个虚函数表指针在32位系统中占4bit。
记住:
带多态性质的基类应该声明一个虚(virtual)析构函数。如果类带任何虚函数,它就应该拥有一个虚析构函数。
类的设计目的不是做基类使用,或不是为了具备多态,就不应该声明虚析构函数。