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

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 destructor
	Base() {}
	Base(int a, char b): m_iA(a), m_cB(b) {}
	virtual ~Base() {}

	// virtual functions
	virtual void f()
	{ 
		printf("invork f()\n"); 
	}
	virtual void g()
	{ 
		printf("invork g()\n"); 
	}
	virtual void h()
	{ 
		printf("invork h()\n"); 
	}

	// non-virtual functions
	void 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_iA
	int iA = *((int *)(&b) + 1);
	printf("iA=%d\n", iA);// 1

	// m_cB
	char 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: 9
the size of Base: 9
&b = 0x12ff3c
*(int *)(&b) = 0x60f3e8
*(int*)(*(int *)(&b)) = 0x4b0701
invork f()
invork g()
invork h()
iA=1
cB=x
This is non-virtual  function named test
&Base::test = 0x4aedca
&Base::test2 = 0x4b263c
This is non-virtual  function named test
invork f()
invork g()
This is non-virtual  function named test

*/

参考:

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

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

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

你可能感兴趣的:(C++对象模型的演变及验证 (1))