C++对象模型的演变及验证 (1)

来源:互联网 发布:淘宝店铺模板源代码 编辑:程序博客网 时间:2024/06/05 13:22

2012-11-10 wcdj

关键字:C++对象模型, 访问私有成员, 虚函数, 虚函数表(vtbl), 虚函数表指针(vptr), 类成员函数指针

C++对象模型的演变

在C++中:

有两种类数据成员:(1) static (2) nonstatic

有三种类成员函数:(1) static (2) nonstatic (3)virtual

C++对象模型的演变过程:

(1) 简单对象模型

一个object是一系列的slots,每个slot指向一个members。在这个简单模型中,members本身并不放在object之中,只有指向member的指针才放在object内。

此模型提出了一种思想:使用索引的思想,之后被应用到C++的指向成员的指针思想中

(2) 表格驱动模型

class object本身则内含指向这两个表格的指针,一个指向data member table,一个指向member function table。其中,member function table是一系列的slots,每一个slot指出一个member function;而data member table则直接含有data本身。

此模型也提出了一种思想:member function table的思想成为以后virtual functions的一个有效解决方案

(3) C++对象模型

nonstatic data members被置于每一个class object之内,static data members则被存放在所有的class object之外,并且,static和nonstatic function members也被放在所有的class object之外。

virtual functions则以两个步骤支持之:

[1] 存在一个virtual table。每一个class产生一堆指向virtual functions的指针,并存放在这张表中。(根据虚函数声明的顺序存放)

[2] 每一个class object被添加了一个指针,指向相关的virtual table。(这个指针被称为vptr)

注意:

(a) vptr的设定和重置都有每一个class的constructor、destructor和copy assignment运算符自动完成。

(b) 每一个class所关联的type_info object (用以支持runtime type identification, RTTI)也经由virtua ltable 被指出来, 通常是放在表格的第一个slot处。(注意实践表明可能不是这样)

C++对象模型简单case验证

了解了C++对象模型之后,可以对其进行简单的验证,不同的编译器可能实现有所不同。下面的例子考虑最简单的情况,暂时不考虑继承,主要测试以下几点:

环境:Windows Server 2003, 32位 + VS2008

(1) 根据对象模型计算类成员偏移量, 并通过偏移量来访问类成员,包括类的私有成员;

(2) 定义类成员函数指针, 并通过类成员函数指针访问类成员函数;

/* 2012-11-10 wcdj * C++虚函数表的实例解析 */#include <stdio.h>// 指定按1字节对齐#pragma pack(1)// 定义普通函数指针typedef void(*Fun)(void);// 定义类成员函数指针class Base;typedef void(Base::*CFun)(void);#define callMemFun(obj, pCFun) ( (obj).*(pCFun) )#define pcallMemFun(pobj, pCFun) ( (pobj)->*(pCFun) ) class Base{public:// constructor and destructorBase() {}Base(int a, char b): m_iA(a), m_cB(b) {}virtual ~Base() {}// virtual functionsvirtual void f(){ printf("invork f()\n"); }virtual void g(){ printf("invork g()\n"); }virtual void h(){ printf("invork h()\n"); }// non-virtual functionsvoid test(){ printf("This is non-virtual  function named test\n"); }void test2(){ printf("This is non-virtual  function named test2\n"); }private:int m_iA;char m_cB;};int main(){Base a, b(1, 'x');// 计算类的大小// sizeof(Base) = sizeof(vfptr) + sizeof(m_iA) + sizeof(m_cB) = 4 + 4 + 1 printf("the size of Base: %d\n", sizeof(a));printf("the size of Base: %d\n", sizeof(b));/* [1] 计算类成员偏移量, 并通过偏移量来访问类成员, 包括类的私有成员 */Fun pFun = NULL;// 对象实例地址printf("&b = 0x%x\n", &b);// 虚函数表地址/* 可以发现, 虚函数表的指针存在于对象实例中最前面的位置     * 说明: * 在Inside The C++ Object Model中有注释说, 每一个class所关联的 * type_info object (用以支持runtime type identification, RTTI)也经由 * virtual table 被指出来, 通常是放在表格的第一个slot处*/printf("*(int *)(&b) = 0x%x\n", *(int *)(&b));// 虚函数表中第一个虚函数的地址printf("*(int*)(*(int *)(&b)) = 0x%x\n", *(int*)(*(int *)(&b)));// 通过偏移量来分别获取类的成员// 注意: 虚函数按照其声明的顺序置于虚函数表中// 需要强制转换为函数指针    // Base的destructor函数, 此时不能调用pFun = (Fun)*((int *)*(int *)(&b) + 0);//pFun();// f()pFun = (Fun)*((int *)*(int *)(&b) + 1);pFun();// g()pFun = (Fun)*((int *)*(int *)(&b) + 2);pFun();// h()pFun = (Fun)*((int *)*(int *)(&b) + 3);pFun();// m_iAint iA = *((int *)(&b) + 1);printf("iA=%d\n", iA);// 1// m_cBchar cB = *(char *)((int *)(&b) + 2);printf("cB=%c\n", cB);// x// 注意: 普通成员函数属于类的级别而不属于对象级别b.test();printf("&Base::test = 0x%x\n", &Base::test);printf("&Base::test2 = 0x%x\n", &Base::test2);/* [2] 定义类成员函数指针, 并通过类成员函数指针访问类成员函数 */// 注意区别类成员函数指针和普通函数指针定义的方法CFun pCFun = NULL;// [1]pCFun = &Base::test;((b).*(pCFun))();// [2]pCFun = &Base::f;callMemFun(b, pCFun)();// [3]pCFun = &Base::g;pcallMemFun(&b, pCFun)();// [4]pCFun = &Base::test;pcallMemFun(&b, pCFun)();return 0;}/*output:the size of Base: 9the size of Base: 9&b = 0x12ff3c*(int *)(&b) = 0x60f3e8*(int*)(*(int *)(&b)) = 0x4b0701invork f()invork g()invork h()iA=1cB=xThis is non-virtual  function named test&Base::test = 0x4aedca&Base::test2 = 0x4b263cThis is non-virtual  function named testinvork f()invork g()This is non-virtual  function named test*/

参考:

[1] C++ 虚函数表解析

[2] 类的普通成员函数的指针

[3] 深度探索C++对象模型,Inside The C++ Object Model

原创粉丝点击