虽然可以对虚函数进行实调用,但程序员编写虚函数的本意应该是实现动态联编。在构造函数中调用虚函数,函数的入口地址是在编译时静态确定的,并未实现虚调用。但是为什么在构造函数中调用虚函数,实际上没有发生动态联编呢?

第一个原因,在概念上,构造函数的工作是为对象进行初始化。在构造函数完成之前,被构造的对象被认为“未完全生成”。当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数,那么此时派生类的构造函数并未执行,所调用的函数可能操作还没有被初始化的成员,将导致灾难的发生。

第二个原因,即使想在构造函数中实现动态联编,在实现上也会遇到困难。这涉及到对象虚指针(vptr)的建立问题。在Visual C++中,包含虚函数的类对象的虚指针被安排在对象的起始地址处,并且虚函数表(vtable)的地址是由构造函数写入虚指针的。所以,一个类的构造函数在执行时,并不能保证该函数所能访问到的虚指针就是当前被构造对象最后所拥有的虚指针,因为后面派生类的构造函数会对当前被构造对象的虚指针进行重写,因此无法完成动态联编。

同样的,在析构函数中调用虚函数,函数的入口地址也是在编译时静态决定的。也就是说,实现的是实调用而非虚调用。
考察如下例子。

#include <iostream>
using namespace std;class A
{
public:virtual void show(){cout<<"in A"<<endl;}virtual ~A(){show();}
};class B:public A
{
public:void show(){cout<<"in B"<<endl;}
};int main()
{A a;B b;
}

程序输出结果是:

in A
in A

在类B的对象b退出作用域时,会先调用类B的析构函数,然后调用类A的析构函数,在析构函数~A()中,调用了虚函数show()。从输出结果来看,类A的析构函数对show()调用并没有发生虚调用。

从概念上说,析构函数是用来销毁一个对象的,在销毁一个对象时,先调用该对象所属类的析构函数,然后再调用其基类的析构函数,所以,在调用基类的析构函数时,派生类对象的“善后”工作已经完成了,这个时候再调用在派生类中定义的函数版本已经没有意义了。

因此,一般情况下,应该避免在构造函数和析构函数中调用虚函数,如果一定要这样做,程序猿必须清楚,对虚函数的调用其实是实调用。


参考文献

[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[8.6(P299-P302)]

C++构造函数与析构函数调用虚函数的注意事项相关推荐

  1. C++构造函数和析构函数调用虚函数时都不会使用动态联编

    先看一个例子: #include <iostream> using namespace std;class A{ public:A() {show();}virtual void show ...

  2. 构造函数调用虚函数的问题

    一般情况下,不允许在构造函数或者析构函数中调用虚函数.其实语法上都没有问题,只是会失去多态性. 如果在构造函数中调用虚函数,会先调用父类中的实现,也就失去了多态的性质. class A {public ...

  3. 构造函数为什么不能是虚函数 ( 转载自C/C++程序员之家)

    从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的.问题出来了,如果构造函数是虚的,就需要通过 vtable来调用, ...

  4. 构造函数为什么不能是虚函数

    从存储空间角度看 虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的. 问题出来了,假设构造函数是虚的.就须要通过 vtable来 ...

  5. 为什么构造函数不能声明为虚函数,析构函数可以,构造函数中为什么不能调用虚函数?

    为什么构造函数不能声明为虚函数,析构函数可以,构造函数中为什么不能调用虚函数 构造函数中为什么不能调用虚函数? 第一个理由是概念上的 第二个理由是机械上的. 构造函数不能声明为虚函数的原因是 1 构造 ...

  6. 构造函数不可以是虚函数;析构函数可以是虚函数,也可以是纯虚函数。

    构造函数不可以是虚函数:析构函数可以是虚函数,也可以是纯虚函数. 一:构造函数不能声明为虚函数的原因 1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的. 而在构造 ...

  7. C++继承中构造函数、析构函数调用顺序及虚析构函数

    首先说说构造函数,大家都知道构造函数里就可以调用成员变量,而继承中子类是把基类的成员变成自己的成员,那么也就是说子类在构造函数里就可以调用基类的成员了,这就说明创建子类的时候必须先调用基类的构造函数, ...

  8. 构造函数及析构函数与虚函数的关系

    我们都知道构造函数不能为虚函数,而基类的析构函数一般都要定义为虚函数.今天重新复习了一下,特在此记载,以便查找. 构造函数不能为虚函数主要有以下两点 1.必要性分析: 当定义派生类对象时,它会主动依次 ...

  9. 2020-12-14(全局/静态对象的构造函数和析构函数调用的时机以及地址)

    一般的对象实例化在什么时候实例化的呢? 是不是在main函数运行到那里的时候,然后创建对象,会调用类里面的构造函数. 那当我们遇到全局/静态对象的时候,它是不是也是需要在main函数里面慢慢构造呢? ...

最新文章

  1. 图像降维之MDS特征抽取方法
  2. leetcode 二分查找 Search in Rotated Sorted ArrayII
  3. 计算机组成原理——概述3
  4. Java Review - Queue和Stack 源码解读
  5. 一个关于pynoi游戏的C语言编程
  6. 使用Gradle将JAR工件发布到Artifactory
  7. [转载] python基础:面向对象-封装
  8. qfile.remove 删除已经被加载的文件_Milvus数据管理:删除的实现原理
  9. border英文缩写_第一丶第二丶第三丶第四的英文缩写是什么
  10. 400元DIY实现手机 笔记本 GPS导航
  11. 数据分析(2)——假设检验的详细原理步骤
  12. mysql 留存率_用mysql统计留存率
  13. 我裸辞 转行软件测试 然而没有人要我
  14. 幼麟棋牌进入房间逻辑分析
  15. 史上最“奇葩”相亲男惊呆网友:你那么普通,却那么自信
  16. day03 Http协议Request
  17. the page has expired due to inactivity. please refresh and try again
  18. 开源的价值观与文化的传递
  19. 情境领导者-第六章、产生胜利者 故事
  20. 热图(Heatmap)绘制(matplotlib与seaborn)

热门文章

  1. promise常见错误
  2. POJ 3268 Bookshelf 2 动态规划法题解
  3. Linux Rsync实现文件同步备份(转载)
  4. SMTP协议初探(二)----linux下c编程实现发邮件
  5. IT销售素质 --善于学习
  6. 蓝桥杯 PREV-27 历届试题 蚂蚁感冒
  7. 【软件测试】如何用场景法进行黑盒测试
  8. 《Macbook Pro概要》- Mac的各种功能使用方法
  9. 1022. D进制的A+B (20)-PAT乙级真题
  10. 蓝桥杯 ADV-144算法提高 01背包