C++中, 为什么需要定义析构函数为虚函数

算是一篇小译文把,加上自己的一点总结。

先构造一个类,如下所示:

#include <iostream>#include <stdio.h>using namespace std;#include <iostream>using namespace std;class Base{public:Base(){ cout<<"Constructing Base\n";}// this is a destructor:~Base(){ cout<<"Destroying Base\n";}};class Derive: public Base{public:Derive(){ cout<<"Constructing Derive\n";}~Derive(){ cout<<"Destroying Derive\n";} };int main(){Derive s;return 0;}上面代码在运行的时候,生成Derive的对象的时候调用的构造函数,会首先调用基类的构造函数,所以当函数执行完毕,撤销s 的时候,也会调用Derive的析构函数之后调用Base 的析构函数,也就是说,不管析构函数是不虚函数,派生类对象在撤销的时候,肯定会一次调用基类的析构函数。那我们为什么要将析构函数定义为虚函数呢?

原因是因为多态的存在。

我们大家都知道,在C++ 中,当一个对象销毁时,析构函数是用来对类对象和对象成员进行释放内存和做一些其他的cleanup操作。析构函数靠~符号来区分,~ 出现在 析构函数名字的前面, 当我们去定义一个 虚析构函数时,你只需要简单的的在~符号前面 加一个 virtual标志就可以了。

为什么需要将析构函数声明为 虚函数,我们最好用几个例子来验证一下,我们首先以一个 不使用虚析构函数的例子开始,然后我们使用一个使用析构函数的例子。一旦看到了其中的区别,你就会明白 为什么需要将析构函数声明为虚函数。让我们开始看一下代码。

Example without a Virtual Destructor:

#include <iostream>using namespace std;class Base{public:Base(){ cout<<"Constructing Base\n";}// this is a destructor:~Base(){ cout<<"Destroying Base\n";}};class Derive: public Base{public:Derive(){ cout<<"Constructing Derive\n";}~Derive(){ cout<<"Destroying Derive\n";} };int main(){Base *basePtr = new Derive();delete basePtr;return 0;}运行 输出 是下面这样的:

根据上面的输出,我们可以看到当我们新建一个指向Deriver类型对象指针的时候,构造函数按照正适当的顺序依次调用,但是这里有一个主要问题

当我们删除指向Deriver 的基类指针时, Deriver类中的析构函数没有调用。这里调用的是Base的析构函数(静态联编)这里设计

那何为动态联编和 静态联编?下面讲解一下:

联编是一个计算机程序彼此相关连的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。

将源代码中的函数调用解释为执行特定的函数代码被称为函数名联编(binding).

在编译过程中进行联编被称为静态联编(static binding),又称早期联编(early binding).它对函数的选择是根据基于对象的 指针或者引用来确定的。(即指针或者引用 指向哪个对象就调用哪个对象的相应的函数) 编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic binding), 又称为晚期联编(late binding).

1.为什么有两种类型的联编以及为什么默认为静态联编?如果动态联编让您能够重新定义类方法,而静态联编在这方面很差,为何摒弃静态联编呢?原因有两个—–效率首先来看效率.为使程序能够在运行阶段进行决策,必须采取一些方法来跟踪实际运行中基类指针或引用指向的对象类型,这增加了额外的开销.例如,如果类不会用作基类,则不需要动态联编.同样,如果派生类不重新定义基类的任何方法,也不需要使用动态联编.在这些情况下,使用静态联编更合理,效率也更高.由于静态联编的效率更高,因此被设置为C++的默认选择.Strousstrup说,C++的指导原则之一是, 不要为不使用的我付出代价(内存或者处理时间).仅当程序设计确实 需要虚函数时,才使用它们.接下来看概念模型.在设计类时,可能包含一些不在派生类重新定义的成员函数.不将访函数设置为虚函数有两方面好处:首先效率高;其次,指出不要重新定义该函数.这表明,仅将那些预期将被重新定义的方法声明为虚拟的.

2.虚函数的工作原理通常,编译器处理虚函数的方法是:给每一个对象添加一个隐藏成员.隐藏成员中保存了一个指向函数地址数组的指针.这种数组称为虚函数表(virtual function table, vtbl).虚函数表中存储了为类对象进行声明的虚函数的地址.例如,基类对象包含一个指针,访指针指向基类中所有虚函数地址表.派生类对象将包含一个指向独立地址表的指针.如果派生类提供了虚岁函数的新定义,访虚函数表将保存新函数的地址;如果派生类没有重新定义虚函数.该vtbl将保存函数原始版本的地址.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中.注意,无论类中包含的虚函数是1还是10 个,都只需要在对象中添加1个地址成员,只是表的大小不同而已.

因为有了梦想,我们才能拥有奋斗的目标,

C++中, 为什么需要定义析构函数为虚函数

相关文章:

你感兴趣的文章:

标签云: