首页 > 代码库 > 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++对象模型