【编程开发】AspAsp.NetCGIPHPJspXMLPERLC++C#VCVBDelphiPowerBuilderJAVA汇编数据库编程移动开发其它语言

您现在的位置:首页 > 网络学院 > 编程开发 > C++ > C++基本概念在编译器中的实现

C++基本概念在编译器中的实现

来源:csdn 作者: 日期:2006-08-19

【聚杰网C++】C++基本概念在编译器中的实现  对于C++对象模型,相信很多程序员都耳熟能详。 本文试图通过一个简单的例子演示一些C++基本概念在编译器中的实现,以期达到眼见为实的效果。

  1、对象空间和虚函数

  1.1 对象空间

  在我们为对象分配一块空间时,例如:

  CChild1 *pChild = new CChild1();

  这块空间里放着什么东西?

  在CChild1没有虚函数时,CChild1对象空间里依次放着其基类的非静态成员和其自身的非静态成员。没有任何非静态成员的对象,会有一个字节的占位符。

  如果CChild1有虚函数,VC6编译器会在对象空间的最前面加一个指针,这就是虚函数表指针(Vptr:Virtual function table pointer)。我们来看这么一段代码:

class CMember1 {
public:
CMember1(){a=0x5678;printf("构造 CMember1/n");}
~CMember1(){printf("析构 CMember1/n");}
int a;
};

class CParent1 {
public:
CParent1(){parent_data=0x1234;printf("构造 CParent1/n");}
virtual ~CParent1(){printf("析构 CParent1/n");}
virtual void test(){printf("调用CParent1::test()/n/n");}
void real(){printf("调用CParent1::test()/n/n");}
int parent_data;
};

class CChild1 : public CParent1 {
public:
CChild1(){printf("构造 CChild1/n");}
virtual ~CChild1(){printf("析构 CChild1/n");}
virtual void test(){printf("调用CChild1::test()/n/n");}
void real(){printf("调用CChild1::test()/n/n");}
CMember1 member;
static int b;
};

  CChild1对象的大小是多少?以下是演示程序的打印输出:

---->派生类对象
对象地址 0x00370FE0
对象大小 12
对象内容
00370FE0: 00410104 00001234 00005678
vptr内容
00410104: 004016a0 00401640 00401f70

  CChild1对象的大小是12个字节,包括:Vptr、基类成员变量parent_data、派生类成员变量member。Vptr指向的虚函数表(VTable)就是虚函数地址组成的数组。

  1.2 Vptr和VTable

  如果我们用VC自带的dumpbin反汇编Debug版的输出程序:

dumpbin /disasm test_vc6.exe>a.txt

  可以在a.txt中找到:

?test@CChild1@@UAEXXZ:
00401640: 55 push ebp...
??_ECChild1@@UAEPAXI@Z:
004016A0: 55 push ebp

  可见VTable中的两个地址分别指向CChild1的析构函数和CChild1的成员函数test。这两个函数是CChild1的虚函数。如果打印两个CChild1对象的内容,可以发现它们Vptr是相同的,即每个有虚函数的类有一个VTable,这个类的所有对象的Vptr都指向这个VTable。

  这里的函数名是不是有点奇怪,附录二简略介绍了C++的Name Mangling。

  1.3 静态成员变量

  在C++中,类的静态变量相当于增加了访问控制的全局变量,不占用对象空间。它们的地址在编译链接时就确定了。例如:如果我们在项目的Link设置中选择“Generate mapfile”,build后,就可以在生成的map文件中看到:

0003:00002e18 ?b@CChild1@@2HA 00414e18 test1.obj

  从打印输出,我们可以看到CChild1::b的地址正是0x00414E18。其实类定义中的对变量b的声明仅是声明而已,如果我们没有在类定义外 (全局域) 定义这个变量,这个变量根本就不存在。

  1.4 调用虚函数

  通过在VC调试环境中设置断点,并切换到汇编显示模式,我们可以看到调用虚函数的汇编代码:

16: pChild->test();
(1) mov edx,dword ptr [pChild]
(2) mov eax,dword ptr [edx]
(3) mov esi,esp
(4) mov ecx,dword ptr [pChild]
(5) call dword ptr [eax+4]

  语句(1)将对象地址放到寄存器edx,语句(2)将对象地址处的Vptr装入寄存器eax,语句(5)跳转到Vptr指向的VTable第二项的地址,即成员函数test。

  语句(4)将对象地址放到寄存器ecx,这就是传入非静态成员函数的隐含this指针。非静态成员函数通过this指针访问非静态成员变量。

  1.5 虚函数和非虚函数

  在演示程序中,我们打印了成员函数地址:

printf("CParent1::test地址 0x%08p/n", &CParent1::test);
printf("CChild1::test地址 0x%08p/n", &CChild1::test);

1 2 3 4 下一页

以下相关文章您是否也应该阅读一下 无相关信息
评论   点击查看全部评论
您的评论参与,将为聚杰带来更大的动力!请不要吝啬!
快速回复
请使用文明语言让我们维护健康绿色网络环境!

匿名发表   验证码: