一、多态机理

#include <iostream>class Father
{
public:virtual void Func1(){std::cout << "Fahter::Func1()" << std::endl;}virtual void Func2(){std::cout << "Fahter::Func2()" << std::endl;}virtual void Func3(){std::cout << "Fahter::Func3()" << std::endl;}private:size_t count_;
};class Son : public Father
{
public:virtual void Func2(){std::cout << "Son::Func2()" << std::endl;}
};using FUNC = void (*)();int main()
{Father *pfather = new Father;Son *pson = new Son;// 得到指向虚函数表指针的地址。long *pvptr_father = (long *)pfather;long *pvptr_son = (long *)pson;// 得到指向虚函数表的指针。    long *vptr_father = (long *)(*pvptr_father); long *vptr_son = (long *)(*pvptr_son); FUNC func1_father, func2_father, func3_father;func1_father = (FUNC) * (vptr_father + 0); // 得到虚函数 Father::Func1() 的地址。func2_father = (FUNC) * (vptr_father + 1); // 得到虚函数 Father::Func2() 的地址。func3_father = (FUNC) * (vptr_father + 2); // 得到虚函数 Father::Func2() 的地址。func1_father();func2_father();func3_father();std::cout << std::endl;FUNC func1_son, func2_son, func3_son;func1_son = (FUNC) * (vptr_son + 0); // 得到虚函数 Father::Func1() 的地址。func2_son = (FUNC) * (vptr_son + 1); // 得到虚函数 Son::Func2() 的地址。func3_son = (FUNC) * (vptr_son + 2); // 得到虚函数 Father::Func2() 的地址。func1_son();func2_son();func3_son();return 0;
}

结果:

Fahter::Func1()
Fahter::Func2()
Fahter::Func3()Fahter::Func1()
Son::Func2()
Fahter::Func3()

二、分析

上图介绍了C++多态的实现机理。注意:虚函数表属于类,虚函数表指针属于对象。

子类继承父类时,会得到和父类相同的虚函数表,该表记载着代码区中属于该类的虚函数的起始地址。当子类没有重写虚函数时,程序调用子类的虚函数,就相当于调用父类的虚函数。当子类重写了父类的虚函数时,相当于将子类的虚函数表中的相应虚函数的地址修改为子类虚函数的首地址,从而完成了多态!

三、对象切割

Son sn;
Father fa = sn;
fa.Func1();
fa.Func2();
fa.Func3();

结果:

Fahter::Func1()
Fahter::Func2()
Fahter::Func3()

对于多态,使用指针或者引用,用父类调用子类的函数达到工厂模式的目的。倘若用子类直接赋值给父类,会有什么结果呢。上述代码就展示了最后的结果,并没有调用子类的函数,而是直接调用了父类的函数。

原因是直接用子类给父类对象赋值,子类中的属于父类部分内容会被编译器自动切割出来并拷贝给了父类。

实际上上述操作一共干了两件事情,

1、生成 Father 对象。

2、用 sn 来初始化 Father 对象。显然,编译器并没有将 sn 的虚函数表指针覆盖掉 fa 的虚函数表指针。

四、总结

1、类只有包含了虚函数才存在虚函数表(只有一个),同属于一个类的对象共享虚函数表,但是有各自的vptr(虚函数表指针)。

2、父类含有虚函数,子类就会有一个虚函数表,但是子类的虚函数表只是和父类的虚函数表中的内容相同,所在的内存位置不同。

3、多态是子类赋值给父类的指针或者引用。若直接对象赋值,是对象切割的范畴。

(SAW:Game Over!)

Cpp 对象模型探索 / 继承关系下的虚函数手动调用相关推荐

  1. c++ map 析构函数_C++|类继承关系中的虚函数、虚析构函数、虚基类

    在继承关系中,虚函数.虚析构函数.虚基类中使用的关键字virtual都是在告诉编译器,此处要进行特殊处理: 虚函数:函数重写时的要求编译器动态绑定来实现多多态 : 虚析构函数:当基类指针指向在堆内实现 ...

  2. C++程序运行时内存布局之--无继承情况下的虚函数

    2019独角兽企业重金招聘Python工程师标准>>> 虚函数是C++实现多态的关键,没有虚函数,C++只能是OB,不能完成OO. 本文介绍的是没有继承情况下,带有虚函数的类在内存中 ...

  3. C++中虚函数的理解,以及简单继承情况下的虚函数的表!

    面向对象的三大特征=封装性+继承性+多态性 封装=将客观事物抽象成类,每个类对自身的数据和方法实行权限的控制 继承=实现继承+可视继承+接口继承 多态=将父类对象设置成为和一个或者更多它的子对象相等的 ...

  4. Cpp 对象模型探索 / 系列文章的索引

    一.对象 普通类对象占用的空间. 子类的内存布局. 编译器为对象创建缺省构造函数的条件. 二.虚函数(完) 对象的虚函数表指针的位置. 继承关系下的虚函数手动调用. 虚函数表和虚函数表指针的创建时机. ...

  5. 透过汇编另眼看世界之多继承下的虚函数函数调用

    在我的前一篇文章"透过汇编另眼看世界之函数调用"中,我们通过汇编了解了虚函数调用的全部过程.在本文中我将分析多继承的情况下虚函数调用的情况. 首先还是写一些简单的代码作为本文分析的 ...

  6. 复合继承关系下的构造和析构

    继承关系下的构造和析构 看一下测试代码: /** @filename: Inheritance.cpp* @author: Tanswer* @date: 2018年01月31日 14:59:28* ...

  7. json 反序列化 父子类型_Jaskson精讲第7篇-类继承关系下的JSON序列化与反序列化JsonTypeInfo...

    Jackson是Spring Boot(SpringBoot)默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的, ...

  8. Cpp 对象模型探索 / 类静态成员函数的调用方式

    一.普通静态成员函数的调用方法 栗子: class CA { public:static void func() {} };int main() {CA A;A.func();CA::func();r ...

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

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

最新文章

  1. anaconda安装scrapy失败的解决方法(2020.7.7)
  2. Bean标签范围配置
  3. HDF5数据库和mysql数据库_哪些数据库比较适合实现数据实时入库的需求?
  4. 关于错误error C4430 error C2365 error C2078 error C2440 error C2143的处理。
  5. 长沙.NET技术社区·设计到实现
  6. python默认数据转换_Python_数据类型转换
  7. 【ElasticSearch】es 使用function_score及soft_score定制搜索结果的分数
  8. webservers ajax,jQuery AJax调用asp.net webservers的实现代码
  9. 数据挖掘-二手车价格预测 Task04:建模调参
  10. 远程连接服务器出现 SQL Error (1130): Host IP is not allowed to connect to this MySQL server 错误...
  11. python无法显示饼图
  12. 命令行字体推荐-更纱黑体
  13. 用语音聊天系统源码做语音聊天app开发
  14. 网易互娱-后台开发(支付方向)二面
  15. 薅羊毛php源码,基于AutoJs实现的薅羊毛App专业版源码大分享---更新啦
  16. 2021最新Java面试真题解析!java开发技能掌握
  17. 51单片机c语言程序执行顺序,51单片机程序执行流程详细分析
  18. Java——nefu
  19. 网址出现dns_probe_finished_no_internet
  20. 电脑版适合什么插件HTML,推荐一些好用的Chrome插件

热门文章

  1. DJANGO里让用户自助修改邮箱地址
  2. webkit的几个属性
  3. WebView点击加载的页面中的按钮时不弹出新窗口以及在加载后执行javascript
  4. SqlServer学习笔记【暂】
  5. ant警告 “warning: 'includeantruntime' was not set”解决方法
  6. 关于CS架构文件传输流的问题,文中代码都是转自网上,但可保证代码无无误...
  7. python django admin.site.register注册应用
  8. Azkaban与Oozie的区别及如何调度spark任务
  9. 【脚本整理】docker-compose 部署prometheus + grafana
  10. 【收藏】vuejs学习笔记github地址