考虑下面的程序:

#include <iostream>
using namespace std;class Base
{
public:Base() { cout << "Base Construct" << endl; func(); }~Base() { cout << "Base Destroy" << endl; }void toG() { g(); }virtual void func() { cout << "Base virtual function" << endl; }
private:virtual void g() { cout << "Base Private virtual" << endl; }
};class Derive : public Base
{
public:Derive() : Base() { cout << "Derive Construct" << endl;  func(); }~Derive() { cout << "Derive Destroy" << endl; }void func() { cout << "Derive virtual function" << endl; }
private:void g() { cout << "Derive Private virtual" << endl; }
};int main()
{Derive d;cout << endl;d.toG();cout << endl;cin.get();return 0;
}

如你所见,我们定义了一个基类Base,又定义了一个派生类Derive并以public的形式继承它。

在构造函数中分别输出不同的字符串常量然后调用虚函数func()。

另外在基类中又存在一个toG()函数用来调用函数内部的虚函数g(),通常这里的g函数应该定义在私有成员作用域中,为了实现用同一个基类接口调用不用的派生类虚函数的目的。

让我们来看一下程序的运行结果:

发现什么不对的地方了吗,如果没有,我们一步步分析:

 Derive d;

首先像往常一样,定义了一个派生类的实例化对象,构造的顺序是从基类到派生类。先调用基类的构造函数输出“Base Construct”,又调用了基类的func()函数输出了“Base virtual function“,到这里基类部分构造完成,开始进入派生类的构造阶段,输出”Derive Construct“,又调用了派生类的func()函数输出了”Derive virtual function"。

 d.toG();

我们用派生类的对象调用基类的接口函数toG(),在函数内部调用了派生类的g()函数输出”Derive Private virtual“。

比较上面的结果,不难发现,同样是在基类的作用域下,func()函数调用的是基类的,g()函数却调用的是派生类的。

这正是问题所在,想一想,上述哪种调用结果是符合预期的,为什么会出现这样的不一致?

涉及到虚函数调用的问题基本上都可以用虚函数表来分析解决,在定义派生类对象的时候,因为func和g两个函数都是虚函数且在派生类中都重新定义了,所以虚函数表中函数的地址都会被覆盖成派生类的虚函数的地址,也就是说只要我们用派生类的对象调用这两个函数,会在虚函数表中寻找最佳匹配的函数获得它的函数地址并调用它,不管作用域是在基类还是在派生类,都会调用派生类的相应函数。

然而func()的调用好像并非如此,这就是为什么说不要在构造函数中调用虚函数的原因。

至于为什么会出现这样的结果,我们知道,在派生类对象的构造阶段是先构造其基类部分的,也就是可以理解成在构造基类的时候派生类的部分还不存在,试想,我们又怎么能去调用一个并不存在的东西的子成员呢?编译器也正是假设在构造阶段派生类对象还不存在,所以不会去调用派生类的虚函数。

归结一点,不要在构造函数中调用虚函数,更准确的说法是不要在基类的构造函数中调用虚函数,因为编译器调用的永远都是属于基类的那个虚函数而不是派生类的。

同样的原因可以解释析构函数,因为析构函数的调用时从派生类到基类的,也就是说先销毁派生类独立的那部分,在向上销毁它的基类。那么如果在基类的析构函数中调用了虚函数,此时派生类部分已经被销毁了,又不存在了,那调用的虚函数永远都是基类的那个。

C++学习笔记-----不要在构造函数和析构函数中调用虚函数相关推荐

  1. (c++)5.4构造函数和析构函数中调用虚函数

    在构造函数和析构函数中调用虚函数时,采用静态联编(他们所调用的虚函数是自己类中定义的函数) 如果在自己类中没有实现这个虚函数,则调用的是基类中的虚函数,绝不会是任何在派生类中重定义的虚函数 //构造函 ...

  2. C++核心准则C.82:不要在构造函数或析构函数中调用虚函数

    C.82: Don't call virtual functions in constructors and destructors C.82:不要在构造函数或析构函数中调用虚函数 Reason(原因 ...

  3. C++中最好不要在构造函数和析构函数中调用虚函数!!!

    1.最好不要在基类和派生类的构造和析构函数中调用虚函数,不会出现多态性 实例如下: #include "iostream"using namespace std;class Bas ...

  4. C++中最好不要在构造函数和析构函数中调用虚函数

    1.最好不要在基类和派生类的构造和析构函数中调用虚函数,不会出现多态性 实例如下: #include "iostream"using namespace std;class Bas ...

  5. C++对象模型8——构造函数和析构函数中对虚函数的调用、全局对象构造和析构、局部static数组的内存分配

    一.构造函数和析构函数中对虚函数的调用 仍然以https://blog.csdn.net/Master_Cui/article/details/109957302中的代码为例 base3构造函数和析构 ...

  6. 不要在构造和析构函数中调用虚函数

    构造函数中不能有虚函数的原因: 原因一:基类构造期间虚函数不会下降到派生类阶段,也就是本身我们声明一个派生类对象,它应该先去调用基类的构造,若此时基类构造里面有虚函数,则这个虚函数是基类的虚函数,而不 ...

  7. C++学习笔记-----永远不要在派生类中改变虚函数的默认参数值

    提到虚函数,我们就会自然而然的想到多态,但是当虚函数中存有默认参数值的时候,在派生类中重定义这个虚函数时不可以改变这个参数的值. 请看下面的例子: #include "stdafx.h&qu ...

  8. 在构造函数/析构函数中调用virtual函数带来的影响

    在构造函数/析构函数中调用virtual函数,那么调用的一定是本类中的virtual函数. 先看一段代码: #include<iostream>class Base { public:Ba ...

  9. C++:构造函数和析构函数能否为虚函数

    C++:构造函数和析构函数能否为虚函数? 简单回答是:构造函数不能为虚函数,而析构函数可以且常常是虚函数. (1) 构造函数不能为虚函数 让我们来看看大牛C++之父 Bjarne Stroustrup ...

最新文章

  1. js左侧三级菜单导航代码
  2. Linux环境变量的设置和查看方法
  3. uniapp uni.request GET方式请求,不能直接传数组解决方法
  4. Excel 一键上传到数据库
  5. FeatureLayer.FeatureClass.Feature --以及图层最容易理解的讲解;如有巧合,一定是别人抄袭(Arcgis辅助理解)
  6. 中国天然蜡乳液行业市场供需与战略研究报告
  7. 进阶12 多线程、等待唤醒机制、线程池
  8. python常用内置函数整理
  9. jQuery 中的 attr
  10. 装饰模式(Decorate Pattern)
  11. 项目中常用到的正则(价格千位分割格式化,手机号3-4-4格式化,密码验证,去除空格,获取url参数,检测24小时时间制,检测url前缀,检测中文,检测手机号,英文单词前后加空格,判断版本号)
  12. 【Linux】安装网易云全攻略
  13. html设置超链接位置,怎么调超链接的位置html
  14. 基于小程序同城交易系统设计 同城小程序校园二手交易小程序毕业设计课题选题毕设毕业设计论文
  15. 爬虫工程师是干嘛的?Python爬虫工程师需要掌握哪些技能?
  16. 高清网络视频监控系统中交换机的选择
  17. cad2018致命错误unhandled_CAD--致命错误unhandled access violation
  18. Part III.S3. 对方案有偏好的直觉模糊多属性决策方法
  19. 解读!《国家职业教育改革实施方案》要点来啦!
  20. 数据分析师进阶必备6大数学利器

热门文章

  1. 如何在服务器上运行python程序_在服务器上配置运行(每天一则段子python程序)...
  2. mysql in or索引失效_in 索引失效的问题
  3. 2008年清华大学计算机研究生机试真题
  4. javaWeb(1)
  5. 第六章 SpringCloud之Ribbon负载均衡
  6. Docker系列(一):容器监控工具Weave Scope安装
  7. mac下日期、时间戳互转
  8. java field, property,variable及getField和getDeclaredField的区别
  9. why xml sucks
  10. 大连.Net俱乐部已经加入INETA