单继承的虚函数类 虚表的一些讨论
来源:互联网 发布:测试两台服务器网络 编辑:程序博客网 时间:2024/04/30 14:56
一般的书上都说,虚函数是在运行时根据对象的实际类型“动态决定”函数入口。但什么是“动态决定”呢?实际上C++编译器在实现这个功能的时候,并非真的 等到虚函数被调用时才去判断这个对象是什么类型的。下面我用一个简单的图表来说明C++编译器到底干了些什么。假设有两个类
struct Base {
virtual void f();
virtual void g();
};
struct Derived : public Base {
virtual void f();
virtual void g();
};
Base 和 Derived 各有一个虚表,分别是 VTable_B 和 VTable_D,那么编译器是怎么通过只用一个虚表指针来实现下面的“动态”调用呢?
Base *pB = new Derived();
pB->f();
先让我们看Base和Derived对象是怎么存储的,以及两个类的虚表结构
Base: VTable_B:
------------ -------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
------------
Derived: VTable_D:
------------ --------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
+---------+
| Derived的|
| 数据 |
------------
pB 指针既可以指向 Base 对象,也可以指向 Derived 对象,所以 pB 本身是不确定的。但是,任何对象本身却从被 new 出来开始就是确定的,所以 Base 对象在构造时,编译器会往 vptr 中填上 VTable_B 的地址,而 Derived 对象在构造时,编译器会往 vptr 中填上 VTable_D 的地址。
等到虚函数被调用的时候,也就是 pB->f() 这行语句被执行的时候,编译器并不需要知道 pB 到底是指向 Base 还是 Derived ,它只要直接用 vptr 就能找到正确的虚表和虚函数入口了,父类和子类的虚表结构是相似的,同一个虚函数入口在父表和子表的偏移量都是一样的。
通过上面这些介绍,我想你应该能理解,为什么在单一继承的条件下,不管有多少层继承,每个对象只需一个 vptr 就行了。
多重继承的条件下,一个 vptr 行不行呢?
不行。多重继承的时候,虚函数既有来自父类1的,也有来自父类2的,所以这些虚函数入口是不能放在同一个虚表当中的。假设 Derived 除了 Base外,还继承 Base2,并且 Base2 中有两个虚函数 x() 和 y (),那么 Derived 对象的存储结构也许是这样的(只是大概,和具体编译器相关)。
Derived: VTable_D:
------------ --------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
+---------+
| vptr2 | VTable_D2:
+---------+ -------------
| Base2的 | | x() 入口 |
| 数据 | +-----------+
+---------+ | y() 入口 |
| Derived的| -------------
| 数据 |
struct Base {
virtual void f();
virtual void g();
};
struct Derived : public Base {
virtual void f();
virtual void g();
};
Base 和 Derived 各有一个虚表,分别是 VTable_B 和 VTable_D,那么编译器是怎么通过只用一个虚表指针来实现下面的“动态”调用呢?
Base *pB = new Derived();
pB->f();
先让我们看Base和Derived对象是怎么存储的,以及两个类的虚表结构
Base: VTable_B:
------------ -------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
------------
Derived: VTable_D:
------------ --------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
+---------+
| Derived的|
| 数据 |
------------
pB 指针既可以指向 Base 对象,也可以指向 Derived 对象,所以 pB 本身是不确定的。但是,任何对象本身却从被 new 出来开始就是确定的,所以 Base 对象在构造时,编译器会往 vptr 中填上 VTable_B 的地址,而 Derived 对象在构造时,编译器会往 vptr 中填上 VTable_D 的地址。
等到虚函数被调用的时候,也就是 pB->f() 这行语句被执行的时候,编译器并不需要知道 pB 到底是指向 Base 还是 Derived ,它只要直接用 vptr 就能找到正确的虚表和虚函数入口了,父类和子类的虚表结构是相似的,同一个虚函数入口在父表和子表的偏移量都是一样的。
通过上面这些介绍,我想你应该能理解,为什么在单一继承的条件下,不管有多少层继承,每个对象只需一个 vptr 就行了。
多重继承的条件下,一个 vptr 行不行呢?
不行。多重继承的时候,虚函数既有来自父类1的,也有来自父类2的,所以这些虚函数入口是不能放在同一个虚表当中的。假设 Derived 除了 Base外,还继承 Base2,并且 Base2 中有两个虚函数 x() 和 y (),那么 Derived 对象的存储结构也许是这样的(只是大概,和具体编译器相关)。
Derived: VTable_D:
------------ --------------
| vptr | | f() 入口 |
+---------+ +----------+
| Base的 | | g() 入口 |
| 数据 | -------------
+---------+
| vptr2 | VTable_D2:
+---------+ -------------
| Base2的 | | x() 入口 |
| 数据 | +-----------+
+---------+ | y() 入口 |
| Derived的| -------------
| 数据 |
------------
http://www.cnblogs.com/kanego/articles/2390238.html
0 0
- 单继承的虚函数类 虚表的一些讨论
- 关于虚函数表的一些讨论
- 单继承条件下的虚函数列表
- c++单继承、多继承、菱形继承的内存布局(虚函数表结构)
- 关于继承的问题的一些讨论
- C++中关于虚函数接口继承与实现继承的讨论
- 单例与static的一些讨论
- C++继承 派生类中的内存布局 以及虚函数、虚函数表的一些总结
- 探索 带有虚函数的单继承的类层次的子类对象的构造过程
- C++虚函数实现&&单继承和多继承下的虚函数布局
- C++中包含有虚函数的单继承状态下的类的内存布局
- C++中包含有虚函数的单继承状态下的类的内存布局
- 继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,菱形虚拟继承
- C++带有虚函数的单继承类的构造过程探索,msvc和gcc编译器
- 单继承与虚函数表
- 单继承与虚函数表
- C++ 虚函数表 单继承
- 虚函数:特殊函数的讨论
- java 复习 (四)
- 分布式系统开发里必须要解决的3个技术问题
- 不容错过!开发者必备的十二大Android开发资源
- 用PING来查看TTL值判断操作系统
- CSS样式模块化组件化
- 单继承的虚函数类 虚表的一些讨论
- 大牛
- JS getElementsByName Span
- hdu 1596 find the safest road
- deep_c++:c++对象模型
- Qt当前路径
- Git学习研究小总结
- 浏览器缓存详解:expires,cache-control,last-modified,etag详细说明
- Android NDK 开发教程二:概述