为什么基类通常都定义一个虚析构函数?

回答这个问题之前,有一个更为宽泛的问题: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

 

posted on 2021-02-02 16:16  QzZq  阅读(235)  评论(0)    收藏  举报

导航