看到侯捷老师的深度探索C++对象模型中data语义学中单一继承而且没有虚继承的数据布局中Concrete1、Concrete2、Concrete3的对象布局,发现在MAC系统下这种继承的方式与老师的观点不一致。特提出来供大家讨论。
侯捷老师在书中P105的图为:
而在MAC下单一继承而且没有虚继承的数据布局与上面有明显的不同。啥也不说,还是直接上代码吧!
#include <iostream> using namespace std; class typedefEx_1{ public: typedefEx_1(int para,char cpara):t(para),i(cpara){}; int geta(){return a;}; private: static int a; int t; char i; }; class typedefEx_2:public typedefEx_1{ public: typedefEx_2(int para,char cpara,char _cpara):typedefEx_1(para,cpara),j(_cpara){}; void print(){cout<<"typedefEx_2!"<<endl;} private: char j; }; class typedefEx_3:public typedefEx_2{ public: typedefEx_3(int para,char cpara,char _cpara,char kpara):typedefEx_2(para,cpara,_cpara),k(kpara){}; void print(){cout<<"typedefEx_3!"<<endl;} private: char k; }; class typedefEx_4:public typedefEx_3{ public: typedefEx_4(int para,char cpara,char _cpara,char kpara,char _kpara):typedefEx_3(para,cpara,_cpara,kpara),m(_kpara){}; private: char m; }; int typedefEx_1::a=0; int main (int argc, const char * argv[]) { cout<<"typedefEx_1的size:"<<sizeof(typedefEx_1)<<endl; cout<<"typedefEx_2的size:"<<sizeof(typedefEx_2)<<endl; cout<<"typedefEx_3的size:"<<sizeof(typedefEx_3)<<endl; cout<<"typedefEx_4的size:"<<sizeof(typedefEx_4)<<endl; typedefEx_4 tt(1,10,0,0,0); typedefEx_4* tptr=&tt; cout<<"tptr的地址为:"<<((int*)tptr)<<endl; cout<<"tptr+4(注意为char型)的数据为:"<<*((char*)tptr+4)<<endl; cout<<"tptr+1(注意为int型)的数据为:"<<*((int*)tptr+1)<<endl; typedefEx_4 tt1(1,'a','b','c','d'); typedefEx_4* tptr1=&tt1; cout<<"tptr1的地址为:"<<((int*)tptr1)<<endl; cout<<"tptr1+4(注意为char型)的数据为:"<<*((char*)tptr1+4)<<endl; cout<<"(char*)((int*)tptr1+1)+1)(注意为开始为int指针,后转换为char型)的数据为:"<<*((char*)((int*)tptr1+1)+1)<<endl; cout<<"tptr1+1(注意为int型指针)的数据为:"<<*((int*)tptr1+1)<<endl; return 0; }
编译运行的结果为:
typedefEx_1的size:8
typedefEx_2的size:8
typedefEx_3的size:8
typedefEx_4的size:8
tptr的地址为:0x7fff5fbffaa0
tptr+4(注意为char型)的数据为:
//注意这里换行,是因为tptr+4(注意为char型)的数据的ASCII值为10,即为换行符‘\n’
tptr+1(注意为int型)的数据为:10
tptr1的地址为:0x7fff5fbffa90
tptr1+4(注意为char型)的数据为:a
(char*)((int*)tptr1+1)+1)(注意为开始为int指针,后转换为char型)的数据为:b
tptr1+1(注意为int型指针)的数据为:1684234849
解析:
从typedefEx_1、typedefEx_2、typedefEx_3、typedefEx_4的size可以看到侯捷老师书中的观点有失偏颇,并不是如他分析的那样为8、12、16。不过侯捷老师对c++继承对象布局的分析还是有一定的道理,如下面用指针来取出相应的值出现了一定的混乱:
tptr+1(注意为int型)的数据为:10,实际取出来的值为0x000A,即i、j、k、m的值分别为10,0,0,0,而采用int型指针来获得的值为10,这是intel cpu小端模式的结果。这一点在tptr1+1(注意为int型指针)的数据为:1684234849(即为a+b*16+c*16^2+d*16^3)得到验证。
因此,老师的担心也得到了印证。如果mac c++编译器按照老师分析的方法来存储,那么即使采用int型指针来取值,也不会出现莫名其妙的结果。该程序在Visual C++编译器中运行的结果与侯捷老师分析的结果一致。这里就不赘述了。
PS:typedefEx_4对象的布局
/*=================================
根据typedefEx_4的结果可以知道,i,j,k,m放在同一个DWORD中,节省了空间,输出的是10,这是因为intel平台的cpu存储方式为小端模式,即指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中(所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中)
而另外三种类对象布局为:
综上所述:我们可以得出c++ standard给了编译器软件开发者一定的自由空间,这种自由空间可能导致相同的程序在不同的编译器下运行得出的结果完全不同,因此我们在实际编程时一定要多加注意,多了解相关编译器的内部操作。