您的位置:首页 > 编程语言 > C语言/C++

深入探索c++虚函数继承模型

2016-06-08 22:07 561 查看
关于c++虚函数的继承模型,在http://blog.csdn.net/haoel/article/details/1948051/一文中有详细的介绍,本文只是对虚函数继承的验证,具体如下。

示例代码:

<pre name="code" class="cpp">#include<stdlib.h>
#include<iostream>
#include<fstream>
#include<stdio.h>
#include<time.h>
#include<memory.h>
#include<math.h>
#include<windows.h>
#include<math.h>
#include<conio.h>
#include<iomanip>
#include<string>
#include<vector>
using namespace std;

class Base {
public:
int a;
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};

class Derived : public Base{
public:
int b;
virtual void f() { cout << "Derived::f" << endl; }
virtual void g1() { cout << "Derived::g1" << endl; }
virtual void h1() { cout << "Derived::h1" << endl; }
};

int main(){
typedef void(*Fun)(void);//Fun是一个函数指针的类型声明,形参表为空,返回值也为空
Base b;
b.a = 11;
Fun pFun = NULL;
cout << "对象的地址是:" << &b << endl;//整个对象b的地址
cout << "对象b中a的值是:" << *((int *)(&b)+1) << endl;//对象b中数据成员a的值
cout << "虚函数表地址:" << (int *)*(int*)(&b) << endl;//虚函数表的地址
//先得到vptr即虚表数组首地址,然后解引用得到vtbl数组中的第一个函数地址
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1 << endl;
cout << "虚函数表 — 第三个函数地址:" << (int*)*(int*)(&b) + 2 << endl;

pFun = (Fun)*((int*)*(int*)(&b));//将第一个函数地址赋值给pfun
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 0); // Base::f()
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1); // Base::g()
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2); // Base::h()
pFun();

Derived c;
c.a = 22;
cout << "对象的地址是:" << &c << endl;
cout << "虚函数表地址:" << (int*)(&c) << endl;
//先得到vptr即虚表数组首地址,然后解引用得到vtbl数组中的第一个函数地址
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&c) << endl;
cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&c) + 1 << endl;
cout << "虚函数表 — 第三个函数地址:" << (int*)*(int*)(&c) + 2 << endl;
cout << "虚函数表 — 第四个函数地址:" << (int*)*(int*)(&c) + 3 << endl;
cout << "虚函数表 — 第五个函数地址:" << (int*)*(int*)(&c) + 4 << endl;

pFun = (Fun)*((int*)*(int*)(&c));//将第一个函数地址赋值给pfun
pFun();
pFun = (Fun)*((int*)*(int*)(&c) + 0); // Base::f()
pFun();
pFun = (Fun)*((int*)*(int*)(&c) + 1); // Base::g()
pFun();
pFun = (Fun)*((int*)*(int*)(&c) + 2); // Base::h()
pFun();
pFun = (Fun)*((int*)*(int*)(&c) + 3); // Base::h()
pFun();
pFun = (Fun)*((int*)*(int*)(&c) + 4); // Base::h()
pFun();
cout << sizeof(b) << endl;//一个基类对象所占空间的大小
cout << sizeof(c) << endl;//一个派生类对象所占大小
cout << sizeof(Derived) << endl;
return 0;
}
运行结果:




当我们在



pFun = (Fun)*((int*)*(int*)(&c) + 4); // Base::h()
pFun();
后面加上

pFun = (Fun)*((int*)*(int*)(&c) + 5); // Base::h()
pFun();
之后,运行结果如下:



通过结果对比我们可以确定

1.在基类中对象b所占空间大小为8个字节(虚函数表指针+int型变量a),派生对象c所占空间大小为12字节(虚函数表指针4个字节+两个int整形的值);

2.基类的虚函数表一共占12个字节(包含三个函数的入口地址,每个函数的入口地址占4个字节),派生类虚函数表大小为20字节(重写了基类的f()函数的入口地址+基类中g()和h()的入口地址+派生类对象自身的g1()和h1()函数入口地址)

3.当偏量加到5时,出现访问冲突,说明派生类的虚函数表只有5个函数的入口地址;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息