C++ 虚表虚函数怎么就实现了多态?
虚表vftable,编译器为每个拥有虚函数的类都建有一张虚函数表,里面存有虚函数的入口指针(地址)。在类对象的内存布局中,先是一个vfptr虚表指针,指向虚表首地址,而后通过偏移量的形式来访问虚表中的地址。
看许多文章都在那里侃侃而谈,然能实际展示类(对象)内存布局者寥寥,不可见内里实现的终究是借他人文字的空想。now, 我们来一窥究竟!
PS:如何利用VS查看类内存布局见文末链接
1. 带虚函数类内存布局

2. 发生单继承时,派生类内存布局,先是复制一份基类内存布局,然后是自己的布局(注意内存对齐)。虚表指针指向自己的虚表,派生类虚函数地址如果自己未覆盖,那么就是基类的,否则是自己的函数地址。

3. 发生多继承时:先按照继承顺序,从左到右排布基类的布局包括虚标指针,然后排布自己的指针和数据;派生类虚表排布形式是按照继承顺序,是继承来的虚函数,如果有覆盖则换成自己的函数地址;然后是下一个基类,直至基类排布完毕。继承来的多张表是独立的(从内存布局中的多个虚表指针可以看出),且使用首地址+偏移量的形式来访问。


4. 发生虚继承时:无论是对象内存排布还是虚表,虚基类的部分都被放到最后排布,且如果派生类有自己的虚函数则会加在第一个基类的虚表末尾。
除vfptr和vftable之外,增加了vbtable 虚基类表(存放继承的虚基类的地址)和 vbptr(指向虚基类表的指针)

测试代码
#include <iostream>
class Base
{
int a;
int b;
public:
virtual void foo() {};
virtual void bar() {};
virtual void bar2(){};
virtual void bar3(){};
}; class Base2
{
int d;
public:
virtual void foo() {};
}; class Derived : virtual public Base, public Base2
{
int c;
public:
//void foo() {};
void bar() override {};
virtual void bar4() {};
}; int main()
{
Base b;
Derived d;
return ;
}
测试代码
多态原理?个人理解
1. C++多态动态建立在虚函数上,使用virtual关键字指明不要在编译器绑定函数地址,而是在运行时访问虚函数表,即动态绑定。
2. 使用基类指针指向派生类对象且调用虚函数时(前提是派生类覆盖了基类虚函数),运行时该指针指向的地址是派生类对象地址,在派生类对象地址头就是指向虚函数表的vfptr,这张虚表自然是派生类的,而在构造虚表的过程中该虚函数早就被派生类自己的函数地址所覆盖,所以调用函数自然是派生类的函数。
3. 多态的含义:基类指针可以指向基类对象以及不同派生类对象,实现了一种写法,多种访问方式的效果,称为多态。
【参考资料】
如何利用VS查看类内存排布:https://www.cnblogs.com/jerry19880126/p/3616999.html
(在VS项目右键属性->C/C++->命令行->添加 /d1 reportAllClassLayout 应用即可,注意debug/release还有平台类型)
C++多态以及虚函数的不错文章:https://www.cnblogs.com/cxq0017/p/6074247.html
C++ 虚表虚函数怎么就实现了多态?的更多相关文章
- 4.6 C++抽象基类和纯虚成员函数
参考:http://www.weixueyuan.net/view/6376.html 总结: 在C++中,可以通过抽象基类来实现公共接口 纯虚成员函数没有函数体,只有函数声明,在纯虚函数声明结尾加上 ...
- 4.2 C++虚成员函数
参考:http://www.weixueyuan.net/view/6371.html 总结: virtual关键字仅用于函数声明,如果函数是在类外定义,则不需要再加上virtual关键字了. 在C+ ...
- 2014 0416 word清楚项目黑点 输入矩阵 普通继承和虚继承 函数指针实现多态 强弱类型语言
1.word 如何清除项目黑点 选中文字区域,选择开始->样式->全部清除 2.公式编辑器输入矩阵 先输入方括号,接着选择格式->中间对齐,然后点下面红色框里的东西,组后输入数据 ...
- C++学习 - 虚表,虚函数,虚函数表指针学习笔记
http://blog.csdn.net/alps1992/article/details/45052403 虚函数 虚函数就是用virtual来修饰的函数.虚函数是实现C++多态的基础. 虚表 每个 ...
- [转]Java中继承、多态、重载和重写介绍
什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承.多态.重载和重写. 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上 ...
- 4.5 C++重载、覆盖和遮蔽
参考:http://www.weixueyuan.net/view/6375.html 总结: 函数签名包括函数名和函数参数的个数.顺序以及参数数据类型. 需要注意的是函数签名并不包含函数返回值部分, ...
- C++构造函数中不能调用虚函数
在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...
- C++之虚函数和多态
干货较多-需要自己深思理解: C++支持两种多态性: 1.编译时多态性(静态绑定-早绑定) 在程序编译阶段即可以确定下来的多态性 通过使用 重载机制(重载函数)实现 (模板)http://blog.c ...
- C++学习基础十二——纯虚函数与抽象类
一.C++中纯虚函数与抽象类: 1.含有一个或多个纯虚函数的类成为抽象类,注意此处是纯虚函数,而不是虚函数. 2.如果一个子类继承抽象类,则必须实现父类中的纯虚函数,否则该类也为抽象类. 3.如果一个 ...
随机推荐
- flutter的加载弹框
代码组件: import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'packa ...
- QML工程加载main.qml的两种方式
1. QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (e ...
- 研究 node lzma 的压缩解压缩
/ eslint-disable / // 压缩为 lzma var fs = require('fs'); var lzma = require('lzma-native'); var compre ...
- 报错:The specified datastore driver ("com.mysql.jdbc.Driver") was not found in the CLASSPATH. Please check your CLASSPATH specification, and the name of the driver.
报错背景: CDH中集成hive插件,启动报错. 报错现象: [main]: Metastore Thrift Server threw an exception... javax.jdo.JDOFa ...
- LODOP设置打印份数及是否逐份输出
LODOP中通过SET_PRINT_COPIES可以设置打印份数,例如:LODOP.SET_PRINT_COPIES(2);//指定份数为2份 如果一个任务里有多页,打印份数的时候,有两种输出方式,一 ...
- 切实解决socket连接掉线检测
原文:切实解决socket连接掉线检测 版权声明:欢迎转载,但是请保留出处说明 https://blog.csdn.net/lanwilliam/article/details/51698807 新公 ...
- Django 之上下文处理器和中间件
一.上下文处理器 上下文处理器是可以返回一些数据,在全局模板中都可以使用.比如登录后的用户信息,在很多页面中都需要使用,那么我们可以放在上下文处理器中,就没有必要在每个视图函数中都返回这个对象. 在s ...
- 多生产者多消费者(第一种方式),基于synchronized,wait,notifyAll
生产者消费者模式描述的是协调与协作关系.比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用 的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者( ...
- C之指针加减运算
法则:1.指针减指针,语法正确,结果得一个整型值,表示两数值之间的对象类型的空间距离,而不是对象之间的字节数差值 2.指针加指针,语法错误, 3.指针加整形值,语法正确,表示后移N个空间单位 ...
- PAT甲级满分有感
时间轴: 2017年,数据结构加入了我的课程清单. 2018年12月,我从网易云课堂下载了数据结构的所有课程视频(学校里没有网,只能离线看),开始一刷.一刷只看了视频,基本没有做题,看到AVL树的时候 ...