首页 > 代码库 > C++对象模型

C++对象模型

今天把C++对象模型重新温习了一遍

 

(我懒,直接把总结cp过来)

总结:

(1) 对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。

(2) 对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。

(3) 虚函数表中,先存放父类的虚函数,再存放子类的虚函数

(4) 如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。

(5) 对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。其中每一个父类拷贝都包含一个虚函数表指针。如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。

(6) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

 

对于虚继承 内存模型则不同

首先是自己的虚函数表,然后是子类的数据成员,然后是4个字节的0x00000000,之后就是父类的虚函数表,之后是父类的数据成员。

简单的可以把这种关系理解为 共享。

 

这里又想到菱形继承。

如果是普通继承

d.print()

 这样一条语句会产生 二义性,原因是因为D 的内存模型中有2份 A 的拷贝,编译器无法决定调用哪个。

可以改为

d.B::print()

d.C::print()

 

同理 如果出现

A* pa = (A*)&d; 

这样的上行转换语句,也会产生二义性。正确写法:

A *pa = (A*)(B*)&d;

A *pa = (A*)(C*)&d;

 

当然 如果使用virtual 声明为虚继承。就省去了这些麻烦同时节省了空间。

 

补充一点: vtable在Linux/Unix 中存放在可执行文件的只读数据段中(rodata)

 

参考资料:

http://blog.csdn.net/haoel/article/details/3081328 // 这篇比较详细 图文并茂

 

http://developer.51cto.com/art/201106/270265.htm

http://blog.chinaunix.net/uid-26611383-id-3772200.html

C++对象模型