多态之虚表剖析1
来源:互联网 发布:钱永健 知乎 编辑:程序博客网 时间:2024/06/08 15:26
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针
如下面代码:
#include <iostream>using namespace std;class B{public: B() { _b = 10; cout << "this = " << this << endl; } virtual void test1() { cout << "B::test1()" <<endl; } virtual void test2() { cout << "B::test2()" << endl; } virtual void test3() { cout << "B::test3()" << endl; }private: int _b;};int main(){ B b; return 0;}
通过对地址进行跟踪:
在vs2013中虚函数地址以全0结束;
既然,只要类中声明虚函数,在创建对象时就会有一个指针指向此类的虚表位置,那么在类的继承体系中,基类中的虚表是如何被继承的呢?
而且,虚表中存放虚函数的地址,那么我们是否可以用一个函数指针不同类去调用它呢?
因此,为了验证虚表中存放的确实是虚函数的地址,我们不用类去调用,用一个函数指针来调用:
下面代码有三个虚函数,分别输出不同结果:
#include <iostream>using namespace std;class B{public: B() { _b = 10; //cout << "this = " << this << endl; } virtual void test1() { cout << "B::test1()" <<endl; } virtual void test2() { cout << "B::test2()" << endl; } virtual void test3() { cout << "B::test3()" << endl; }private: int _b;};typedef void(*FUN_TEST)();int main(){ B b; for (int i = 0; i < 3; ++i)//类中声明了三个虚函数 { /*我们取对象b的地址,b的地址此时指向b的存储空间, 我们已经知道对象的前四个字节存放的是对象中虚表的 指针,将对象的前四个字节强转成int*,即就可将指向 对象的指针转变成指向对象中虚表的地址,再将它强转成 指向函数的函数指针,就可找到虚函数的地址*/ FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&b + i)); fun(); cout << ":" << (int*)fun << endl; } system("pause"); return 0;}
结果:
我们已经知道,在类中只要有虚函数,编译器就会为此类提供一个保存虚函数地址的虚表,那么,在类的继承体系中,这个虚表是如何被继承下来的呢???
1、没有覆盖:
我们用上面同样的方法,用函数指针来调用派生类中的虚函数
class B{public: B() { _b = 10; } virtual void test1() { cout << "B::test1()" <<endl; } virtual void test2() { cout << "B::test2()" << endl; } virtual void test3() { cout << "B::test3()" << endl; }private: int _b;};class D :public B{public: virtual void test4() { cout << "B::test4()" << endl; } virtual void test5() { cout << "B::test5()" << endl; } virtual void test6() { cout << "B::test6()" << endl; }private: int _d;};typedef void(*FUN_TEST)();int main(){ D d; cout << sizeof(d) << endl; for (int i = 0; i < 6; ++i) { FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&d + i)); fun(); cout << ":" << (int*)fun << endl; } system("pause"); return 0;}
结果:
分析:在计算派生类大小为12,而非16。说明在继承时基类和派生类的两个虚表合并了,而输出结果也是先输出基类的虚函数,再输出派生类的虚函数,即在新合并的派生类虚表中对应的模型应该是下面这种:
总结:
1、虚函数按照其声明顺序存在于虚表中
2、在派生类中,前面是基类的虚函数,后面是派生类的虚函数。
2、有覆盖
即在派生类中将基类的虚函数进行重写。
前面代码稍加改造:
class B{public: B() { _b = 10; } virtual void test1() { cout << "B::test1()" <<endl; } virtual void test2() { cout << "B::test2()" << endl; } virtual void test3() { cout << "B::test3()" << endl; }private: int _b;};class D :public B{public: void test1() { cout << "D::test1()" << endl; } virtual void test5() { cout << "D::test5()" << endl; } virtual void test6() { cout << "D::test6()" << endl; }private: int _d;};typedef void(*FUN_TEST)();int main(){ D d; cout << sizeof(d) << endl; for (int i = 0; i < 5; ++i) { FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&d + i)); fun(); cout << ":" << (int*)fun << endl; } system("pause"); return 0;}
结果:
分析不难得到:派生类的虚表生成是先拷贝基类的虚函数表;如果派生类重写了基类的某个虚函数,就替换虚表中同位置的基类虚函数。然后跟上自己定义的虚函数。
0 0
- 多态之虚表剖析1
- 多态之虚表剖析2
- 链表剖析之单链表剖析(一)
- 链表剖析之单链表剖析(二)
- 源码剖析之epoll(1)
- C++多态相关关问题及虚表剖析
- C++多态及虚表深度剖析
- 链表剖析之双向链表剖析
- (补)链表剖析之循环链表剖析
- 剖析多态
- 9、多态剖析
- 9、多态剖析
- 虚函数表剖析
- 虚表剖析
- 虚表剖析
- 块状链表之代码剖析
- 数据机构之-hash表剖析
- Razor视图引擎之语法剖析1
- Activity生命周期
- 二叉树根据前序和中序推出后续
- 数据库中的并发操作带来的一系列问题
- centos6.8安装mysql
- springMVC+mybatis开发的流程
- 多态之虚表剖析1
- Java 中 Comparable 和 Comparator 比较
- Spark之Action
- stl vector介绍
- 母函数 基础入门
- Java泛型对方法重载的影响(一)
- jdk1.7安装和环境变量配置
- C++ 多态
- 详解jquery-validate.js