C++的虚函数表
2015-11-18 09:40
225 查看
这里的例子全部来自陈皓的C++ 虚函数表解析,经过修改的。
编译器:g++ (Ubuntu 4.9.2-10ubuntu13) 4.9.2
环境:ubuntu 15.04 64位系统(地址占8字节)
例子1:
输出:
解释:通过强转对象b的地址,将地址逐个取出来运行看看是什么。
总结:对象b中存储了2个东西,第一个是虚函数表指针,第二个是变量a,所以共计8+8=16字节。在虚函数表指针所指的地址中,有3个指针,分别是3个虚函数的指针,每个占8字节,共计3*8=24字节。陈皓说虚表最后加个标志,这点不知道怎么验证~
验证了如下这副图:
例子2:
一般继承,无函数覆盖的,全部函数都声明为virtual:
输出:
解释:仅仅只有2个类,且Derive继承Base类,一共有6个虚函数。
总结:对象d中一共占用8个字节,也就是只保存了虚函数表的地址。虚函数表中一共有6个函数指针,分别是Base的3个+Derive的3个。
验证了如下这副图:
例子3:
一般继承,只有1个函数是覆盖的,全部函数都声明为virtual:
输出:
解释:这个模型跟上面的差不多,而这次有1个函数是覆盖的。
总结:虚函数表中一共有5个函数指针,Base中占2个,Derive中占3个。流程是先将基类的3个函数摆在虚函数表前面,接着摆派生类的,由于Derive的函数f覆盖掉基类Base的函数f,所以直接代替基类的函数f的位置。多态是:用基类的指针指向派生类对象,调用的是有覆盖的派生类中的函数。这里就可以实现多态~
验证了如下这副图:
例子4:
多重继承,无虚函数覆盖,但全部函数都声明为虚函数。
输出:
解释:Derive类一共有3个基类,按1,2,3的顺序继承。由于有3个基类,所以对象d中有且仅有3个指针,分别指向其基类的虚函数表。
总结:虚函数表1中有5个函数,虚函数表2和3中有3个函数。也就是说,基类Derive中的虚函数是借放在第一个基类的虚函数表中的尾部。
验证了如下这副图:
例子5:
多重继承,有1个虚函数覆盖,全部函数声明为虚函数。
输出:
解释:例子基本和上一个例子一样。基类Derive中的虚函数f 覆盖掉3个基类中的同名虚函数f。
总结:依然是3个函数表,只是每个函数表中的同名函数f 都被替换成了基类Derive中的函数f 。所以虚函数表1中有4个指针,表2和3都分别有3个。
编译器:g++ (Ubuntu 4.9.2-10ubuntu13) 4.9.2
环境:ubuntu 15.04 64位系统(地址占8字节)
例子1:
#define LL long long class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } LL a; }; int main(void) { typedef void (*Fun)(void); Base b; b.a=10086; Fun pFun = NULL; cout << "虚函数表地址:" << (int*)(&b) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl; LL *p=(LL*)*(LL*)&b; for(int i=0; i<3; i++) { pFun = (Fun)*(p+i); pFun(); } cout<<"a的地址: "<<(&b)+1<<endl; cout<<"a的值: "<<*((LL*)(&b)+1)<<endl; cout<<"对象b的大小:"<<sizeof(b)<<endl; return 0; }
输出:
解释:通过强转对象b的地址,将地址逐个取出来运行看看是什么。
总结:对象b中存储了2个东西,第一个是虚函数表指针,第二个是变量a,所以共计8+8=16字节。在虚函数表指针所指的地址中,有3个指针,分别是3个虚函数的指针,每个占8字节,共计3*8=24字节。陈皓说虚表最后加个标志,这点不知道怎么验证~
验证了如下这副图:
例子2:
一般继承,无函数覆盖的,全部函数都声明为virtual:
class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derive:public Base{ public: virtual void f1() { cout << "Derive::f1" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } virtual void h1() { cout << "Derive::h1" << endl; } }; int main(void) { typedef void (*Fun)(void); Derive d; Fun pFun = NULL; cout << "虚函数表地址:" << (LL*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (LL*)*(LL*)(&d) << endl; LL *p=(LL*)*(LL*)&d; for(int i=0; i<6; i++) { pFun = (Fun)*(p+i); pFun(); } cout<<"对象d的大小:"<<sizeof(d)<<endl; return 0; }
输出:
解释:仅仅只有2个类,且Derive继承Base类,一共有6个虚函数。
总结:对象d中一共占用8个字节,也就是只保存了虚函数表的地址。虚函数表中一共有6个函数指针,分别是Base的3个+Derive的3个。
验证了如下这副图:
例子3:
一般继承,只有1个函数是覆盖的,全部函数都声明为virtual:
class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derive:public Base{ public: virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } virtual void h1() { cout << "Derive::h1" << endl; } }; int main(void) { typedef void (*Fun)(void); Derive d; Fun pFun = NULL; cout << "虚函数表地址:" << (LL*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (LL*)*(LL*)(&d) << endl; LL *p=(LL*)*(LL*)&d; for(int i=0; i<5; i++) { pFun = (Fun)*(p+i); pFun(); } cout<<"对象d的大小:"<<sizeof(d)<<endl; return 0; }
输出:
解释:这个模型跟上面的差不多,而这次有1个函数是覆盖的。
总结:虚函数表中一共有5个函数指针,Base中占2个,Derive中占3个。流程是先将基类的3个函数摆在虚函数表前面,接着摆派生类的,由于Derive的函数f覆盖掉基类Base的函数f,所以直接代替基类的函数f的位置。多态是:用基类的指针指向派生类对象,调用的是有覆盖的派生类中的函数。这里就可以实现多态~
验证了如下这副图:
例子4:
多重继承,无虚函数覆盖,但全部函数都声明为虚函数。
class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive:public Base1,public Base2,public Base3{ public: virtual void f1() { cout << "Derive::f1" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; int main(void) { typedef void (*Fun)(void); Derive d; Fun pFun = NULL; cout << "虚函数表地址:" << (LL*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (LL*)*(LL*)(&d) << endl; LL *p=(LL*)*(LL*)&d; for(int i=0; i<5; i++) { pFun = (Fun)*(p+i); pFun(); } for(int i=0; i<3; i++) { pFun = (Fun)*(p+i); pFun(); } for(int i=0; i<3; i++) { pFun = (Fun)*(p+i); pFun(); } cout<<"对象d的大小:"<<sizeof(d)<<endl; return 0; }
输出:
解释:Derive类一共有3个基类,按1,2,3的顺序继承。由于有3个基类,所以对象d中有且仅有3个指针,分别指向其基类的虚函数表。
总结:虚函数表1中有5个函数,虚函数表2和3中有3个函数。也就是说,基类Derive中的虚函数是借放在第一个基类的虚函数表中的尾部。
验证了如下这副图:
例子5:
多重继承,有1个虚函数覆盖,全部函数声明为虚函数。
class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive:public Base1,public Base2,public Base3{ public: virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; int main(void) { typedef void (*Fun)(void); Derive d; Fun pFun = NULL; cout << "虚函数表地址:" << (LL*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (LL*)*(LL*)(&d) << endl; LL *p=(LL*)*(LL*)&d; for(int i=0; i<4; i++) { pFun = (Fun)*(p+i); pFun(); } for(int i=0; i<3; i++) { pFun = (Fun)*(p+i); pFun(); } for(int i=0; i<3; i++) { pFun = (Fun)*(p+i); pFun(); } cout<<"对象d的大小:"<<sizeof(d)<<endl; return 0; }
输出:
解释:例子基本和上一个例子一样。基类Derive中的虚函数f 覆盖掉3个基类中的同名虚函数f。
总结:依然是3个函数表,只是每个函数表中的同名函数f 都被替换成了基类Derive中的函数f 。所以虚函数表1中有4个指针,表2和3都分别有3个。
相关文章推荐
- C++输入输出操作符重载
- C++输入输出操作符重载
- 【c++】公司职员系统
- 【C++】多态的实现原理
- 单片机C语言代码-代码格式
- C语言之指针高级
- 查询词提示系统的简单实现
- c语言:3种方法;求出0~999之间的所有“水仙花数”并输出。
- 【C】关于C语言数组的总结(1)
- C++ 运算符优先级
- 解决在 WP8/ WP8.1 项目中 引用 C++ 组件时出现的 System.TypeLoadException 错误
- c++ 默认构造函数,构造函数,复制构造函数,赋值操作符,析构函数调用示例
- C++map遍历删除数据(删除被2整除的键值对)
- 回车与换行的区别
- c++使用winsocket创建UDP
- cin 和 getchar()
- C语言练习作业(四)
- 绝不重新定义继承而来的缺省参数值--from Effective c++ item 37
- 【重回C】c语言之顺序表的部分基本操作
- 一起talk C栗子吧(第六十二回:C语言实例--字符串比较)