为什么基类通常都定义一个虚析构函数?
回答这个问题之前,有一个更为宽泛的问题:1、为什么C++需要虚函数?2、为什么需要析构函数?
标题所述的问题其实可以通过如上的两个问题解答。
首先,让我们看看为什么C++需要虚函数?
举一个例子:
class Animal{ public: void eat(){std::cout << "I'm eating generic food"} }; class Cat : public Animal{ public : void eat(){std::cout << "I'm eating a rat."} };
我们在main函数中实现如下代码:
Animal *animal = new Animal; Cat *cat = new Cat; animal->eat(); // Outputs: "I'm eating generic food." cat->eat(); // Outputs: "I'm eating a rat."
我们将Cat的eat方法覆盖Animal的eat方法。
查看结果,貌似这一方法是可行的。
那么,我们换一种写法:
void func(Animal *xyz) { xyz->eat(); }
我们在func函数中传入基类Animal的指针,调用它的eat方法。此时,在我们分别传入Cat与Animal时,结果发生了一些变化:
Animal *animal = new Animal; Cat *cat = new Cat; func(animal); // Outputs: "I'm eating generic food." func(cat); // Outputs: "I'm eating generic food."
可以看出,当func接收Cat对象是,并没有使用自己的eat方法,而是采用其基类的eat方法。
此时,我们引入虚函数virtual关键字:
class Animal { public: virtual void eat() { std::cout << "I'm eating generic food."; } }; class Cat : public Animal { public: void eat() { std::cout << "I'm eating a rat."; } };
得到输出:
func(animal); // Outputs: "I'm eating generic food." func(cat); // Outputs: "I'm eating a rat."
可以看到,引入virtual可以帮助我们动态绑定。
从这里我们就可以清楚看到,派生类对具有virtual关键字的虚函数,可以覆盖函数并实现适合自己的版本,进一步地,在动态绑定的过程中,可以通过基类的指针或引用调用到自己的版本。
接下来,来简单回答一下析构函数的作用。
即,析构函数与构造函数的作用正好相反,为了防止内存泄漏,它用来完成对象被删除前的一些清理工作。
现在,结合析构函数的作用,以及不使用虚函数在使用指针或引用操作对象时存在的问题。我们可以看出虚析构函数的作用如下:
1、当基类的析构函数不声明virtual时,派生类继承基类,通过基类的指针或引用指向派生类时。只会调用基类的析构函数,而不调用派生类的析构函数。
2、当基类的析构函数声明为virtual时,派生类继承基类,通过基类的指针或引用指向派生类时。先调用派生类的析构函数,然后再调用基类的析构函数。
【reference】
[1] https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors
[2] https://blog.csdn.net/derkampf/article/details/62093252
浙公网安备 33010602011771号