带有虚函数的情况。

下面情况编译器也会在需要的时候为其合成。

1.如果一个类自己声明为虚函数.

  1. 1 #include<iostream>
  2. 2 using namespace std;
  3. 3 class Base
  4. 4 {
  5. 5 public:
  6. 6 virtual void foo(){}
  7. 7 };
  8. 8 int main()
  9. 9 {
  10. 10 Base b;
  11. 11 while (1);
  12. 12 return 0;
  13. 13 }

分析:由于类中声明了虚函数,所以编译器会为其合成一个出来,这个合成的默认构造函数的作用是用来保存虚函数表的指针。

2.如果一个类继承了带虚函数的类。

1)单继承情况

  1. 1 #include<iostream>
  2. 2 using namespace std;
  3. 3 class Base
  4. 4 {
  5. 5 public:
  6. 6 virtual void foo(){}
  7. 7 };
  8. 8 class Deprive:public Base
  9. 9 {
  10. 10 public:
  11. 11 void foo() override {
  12. 12
  13. 13 }
  14. 14 };
  15. 15 int main()
  16. 16 {
  17. 17 Deprive d;
  18. 18 while (1);
  19. 19 return 0;
  20. 20 }

分析:由于继承关系,所以类Base的虚函数属性也被Deprive继承下来,由于类Deprive中没有自己的构造函数,所以此时编译器会为其合成一个出来,同样,在这个合成的默认构造函数中,所做的工作是保存虚函数表的指针。(这里通过sizeof(Deprive)可以看出,类的大小还是4。也就是说,类Base和类Deprive共用了一个虚函数表指针).

2)多继承情况

  1. 1 #include<iostream>
  2. 2 using namespace std;
  3. 3 class Base1
  4. 4 {
  5. 5 public:
  6. 6 virtual void foo1(){}
  7. 7 virtual void foo1(){}
  8. 8 };
  9. 9 class Base2
  10. 10 {
  11. 11 public:
  12. 12 virtual void func1(){}
  13. 13 virtual void func2(){}
  14. 14 };
  15. 15 class Deprive:public Base1,public Base2
  16. 16 {
  17. 17 public:
  18. 18 void foo1() override {}
  19. 19 void func2()override {}
  20. 20 virtual void My_HanShu(){}
  21. 21 };
  22. 22 int main()
  23. 23 {
  24. 24 Deprive d;
  25. 25 while (1);
  26. 26 return 0;
  27. 27 }

分析:同样,在多继承体系中,编译器会为类Deprive合成一个默认的构造函数出来,用来保存虚函数表的地址,这里通过sizeof(Deprive)可以看出大小为8字节,所以在默认的构造函数中有两个虚函数表指针,一个是与基类共用的,另一个是另外一个基类的。

注:多继承时,按照继承的顺序,子类的虚函数表指针与第一个继承的父类共用。

3.类派生自一个继承链串。

  1. 1 #include<iostream>
  2. 2 using namespace std;
  3. 3 class Base1
  4. 4 {
  5. 5 public:
  6. 6 virtual void foo1(){}
  7. 7 };
  8. 8 class Base2:public Base1
  9. 9 {
  10. 10 public:
  11. 11 };
  12. 12 class Deprive:public Base2
  13. 13 {
  14. 14 public:
  15. 15 };
  16. 16 int main()
  17. 17 {
  18. 18 Deprive d;
  19. 19 while (1);
  20. 20 return 0;
  21. 21 }

分析:这种情况本质没有什么区别,只要基类有虚函数在,那么不管他的派生类的链串有多长,虚函数的属性就会被继承,虚函数属性被继承了,接下来的分析就和前面相同了。

下面是C++对象模型中的例子(我添加了一些代码以便运行的效果):(书P45页)

  1. 1 #include<iostream>
  2. 2 using namespace std;
  3. 3 class Widget
  4. 4 {
  5. 5 public:
  6. 6 virtual void flip()=0;
  7. 7 };
  8. 8 void flip(Widget& widget)
  9. 9 {
  10. 10 widget.flip();
  11. 11 }
  12. 12 class Bell:public Widget
  13. 13 {
  14. 14 public:
  15. 15 void flip()
  16. 16 {
  17. 17 cout << "Bell" << endl;
  18. 18 }
  19. 19 };
  20. 20 class Whistle:public Widget
  21. 21 {
  22. 22 public:
  23. 23 void flip()
  24. 24 {
  25. 25 cout << "Whistle" << endl;
  26. 26 }
  27. 27 };
  28. 28 void foo()
  29. 29 {
  30. 30 Bell b;
  31. 31 Whistle w;
  32. 32 flip(b);
  33. 33 flip(w);
  34. 34 }
  35. 35 int main()
  36. 36 {
  37. 37 foo();
  38. 38 while (1);
  39. 39 return 0;
  40. 40 }

分析:由于编译器在合成的默认构造函数中添加了虚函数表指针,所以接下来在函数void flip(Widget& widget)中的调用是通过虚函数表走的。

widget.flip()的编译器视角就是:*widget.vptr[1](&widget)  (关于布局写法以后再深谈)。

C++构造函数语义学(二)(基于C++对象模型)的更多相关文章

  1. C++构造函数语义学(一)(基于C++对象模型)

    如果一个类没有自己的构造函数,编译器会在需要的时候为其合成一个出来,俗称:合成默认构造函数.但是请注意是在需要的时候,并不是所有情况. 请看下面代码: 1 #include<iostream&g ...

  2. C++构造函数语义学(三)(基于C++对象模型)

    带有虚基类的情况. 1 #include<iostream> 2 using namespace std; 3 class X 4 { 5 public: 6 int i; 7 }; 8 ...

  3. 构造函数语义学之Copy Constructor构建操作(2)

    二.详述条件 3 和 4 那么好,我又要问大家了,条件1 和 2比较容易理解.因为member object或 base class 含有copy constructor.那么member objec ...

  4. VSTO学习笔记(二)Excel对象模型

    原文:VSTO学习笔记(二)Excel对象模型 上一次主要学习了VSTO的发展历史及其历代版本的新特性,概述了VSTO对开发人员的帮助和效率提升.从这次开始,将从VSTO 4.0开始,逐一探讨VSTO ...

  5. 构造函数语义学——Copy Constructor 篇

    构造函数语义学--Copy Constructor 篇 本文主要介绍<深度探索 C++对象模型>之<构造函数语义学>中的 Copy Constructor 构造函数的调用时机 ...

  6. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. Android消息推送(二)--基于MQTT协议实现的推送功能

    国内的Android设备,不能稳定的使用Google GCM(Google Cloud Messageing)消息推送服务. 1. 国内的Android设备,基本上从操作系统底层开始就去掉了Googl ...

  8. word2vec原理(二) 基于Hierarchical Softmax的模型

    word2vec原理(一) CBOW与Skip-Gram模型基础 word2vec原理(二) 基于Hierarchical Softmax的模型 word2vec原理(三) 基于Negative Sa ...

  9. 构造函数语义学——Default Constructor篇

    构造函数语义学--Default Constructor 篇 这一章原书主要分析了:编译器关于对象构造过程的干涉,即在对象构造这个过程中,编译器到底在背后做了什么 这一章的重点在于 default c ...

随机推荐

  1. JAVA微信公众号网页开发——通过接收人的openid发送模板消息

    TemplateData.java 1 package com.weixin.weixin.template; 2 3 public class TemplateData { 4 private St ...

  2. 逆波兰(非与或)表达式原理及C++代码实现

    p.p1 { margin: 0; font: 11px Menlo; color: rgba(209, 47, 27, 1); background-color: rgba(255, 255, 25 ...

  3. 【LeetCode】491. Increasing Subsequences 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  4. C++单元测试框架gtest使用

    作用 作为代码编码人员,写完代码,不仅要保证编译通过和运行,还要保证逻辑尽量正确.单元测试是对软件可测试最小单元的检查和校验.单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成 ...

  5. MySQL 中的共享表空间与独立表空间

    对于 InnoDB 存储引擎来说,它可以将每张表存放于独立的表空间,即tablename.ibd 文件:也可以将数据存放于 ibdata 的共享表空间,一般命名是 ibdataX,后面的 X 是一个具 ...

  6. CapstoneCS5210|HDMI转VGA音视频转接线|CS5210转换器方案芯片

    Capstone最新推出的一款HDMI转VGA音视频转接线或者转换器方案芯片CS5210. 其设计的优势在于内置晶振,外围电路器件较少设计简单,芯片封装集成度较高,方案BOM成本低,相比其他方案产品更 ...

  7. java -jar 指定logback.xml、application.yaml

    java -jar 指定logback.xml -Dlogging.config="C:\logbacs\logback.xml" 示例:java -jar   -Dlogging ...

  8. EMQX源码编译过程

    以emqx4.0.7版本为例 1.安装erlang环境 可以参考:https://www.cnblogs.com/shanfeng1000/p/11951703.html 这里需要注意一下,要按照em ...

  9. Hadoop问题解决记录

    # 1.解决Unable to load native-hadoop library for your platform告警 安装Hadoop启动之后总有警告:Unable to load nativ ...

  10. 来自MyBatis不一样收获结果的探索之旅-v3.5.9

    概述 定义 MyBatis官网 https://mybatis.org/mybatis-3/ 最新版本为3.5.9 MyBatis是一个的ORM框架,支持自定义SQL.存储过程和高级映射.MyBati ...