声明: 内容都是书上的,自己重写下,加深下印象,如有不妥请指出。
参数书籍信息:
<<高质量C/C++>> 第三版 第12章C++面向对象程序设计 电子工业出版社
<<深度探索C++对象模型>> 第1章关于对象 华中科技大学出版社
先看下简单类的代码和数据放置的位置
代码:
int CAnimal::m_snType = 0x12345678; int _tmain(int argc, _TCHAR* argv[]) { CAnimal AnimalObj; CAnimal *lpObj = &AnimalObj; printf("%d,%s", lpObj->m_snType, lpObj->m_szName); return 0; }
代码:
class CAnimal { public: CAnimal() { strncpy(m_szName, "Test!", sizeof(m_szName)); 01322453 mov esi,esp 01322455 push 0Ah 01322457 push offset string "Test!" (13279B0h) 0132245C mov eax,dword ptr [this] 0132245F push eax 01322460 call dword ptr [__imp__strncpy (132B3B4h)] } ~CAnimal() { _asm NOP 00AD210C nop printf("%d,%s", lpObj->m_snType, lpObj->m_szName); 013248E6 mov esi,esp 013248E8 mov eax,dword ptr [ebp-20h] //指针保存的就是类的首址(这里也就字符串的地址) 013248EB push eax 013248EC mov ecx,dword ptr [CAnimal::m_snType (132A00Ch)] //(常量区)至少不再栈中 013248F2 push ecx 013248F3 push offset string "%d,%s" (132783Ch) 013248F8 call dword ptr [__imp__printf (132B3ACh)]
这里Class CAnimal里面其实只有一个成员就是 m_szName
在类中增加析构/虚函数和静态函数,将m_szName改为16byte
代码:
virtual ~CAnimal() virtual bool isCanSay(){} static bool isCanFly(){} main函数中,这次不用指针在保存对象 CAnimal AnimalObj; AnimalObj.isCanSay(); AnimalObj.isCanFly(); printf("class大小:%d ,%d,%s", sizeof(AnimalObj), AnimalObj.m_snType, AnimalObj.m_szName);
代码:
CAnimal AnimalObj; 000348F0 lea ecx,[ebp-28h] //所谓的this,就是class内存块的首地址 000348F3 call CAnimal::CAnimal (310D7h) 000348F8 mov dword ptr [ebp-4],0 AnimalObj.isCanSay(); 000348FF lea ecx,[ebp-28h] 00034902 call CAnimal::isCanSay (312A3h) AnimalObj.isCanFly(); 00034907 call CAnimal::isCanFly (312ADh) printf("class大小:%d ,%d,%s", sizeof(AnimalObj), AnimalObj.m_snType, AnimalObj.m_szName); 0003490C mov esi,esp 0003490E lea eax,[ebp-24h] 00034911 push eax 00034912 mov ecx,dword ptr [CAnimal::m_snType (3A00Ch)] 00034918 push ecx 00034919 push 14h 0003491B push offset string "class\xb4\xf3\xd0\xa1\xa3\xba%d ,%d,%s" (37C08h) 00034920 call dword ptr [__imp__printf (3B3ACh)] virtual ~CAnimal() { _asm NOP 0003210C nop
0312a3 jmp CAnimal::isCanSay (312xxh)
02129e jmp CAnimal::~CAnimal (000321xx)
有单继承的情况
代码:
class CCat : public CAnimal { public: CCat(){m_nTest = 0;} virtual bool isCanSay(){return true;} virtual bool isEatFish(){return true;} int m_nTest; }; CCat CatObj; 00D14930 lea ecx,[ebp-48h] 00D14933 call CCat::CCat (0D112CBh) CCat(){m_nTest = 0;} 00D12390 push ebp 00D14938 mov byte ptr [ebp-4],1 CatObj.isCanSay(); 00D1493C lea ecx,[ebp-48h] 00D1493F call CCat::isCanSay (0D112B2h) 00D112B2 jmp CCat::isCanSay (0D11A20h) //虚表的下标1位置 CatObj.isCanFly(); 00D14944 call CAnimal::isCanFly (0D112ADh) 00D112AD jmp CAnimal::isCanFly (0D11930h) printf("class大小:%d ,%d,%s", sizeof(CatObj), CatObj.m_snType, CatObj.m_szName); 00D14949 lea eax,[ebp-44h] 00D1494C mov esi,esp 00D1494E push eax 00D1494F mov ecx,dword ptr [CAnimal::m_snType (0D1A00Ch)] 00D14955 push ecx 00D14956 push 18h 00D14958 push offset string "class\xb4\xf3\xd0\xa1\xa3\xba%d ,%d,%s" (0D17C08h)
CCat类内内存的变化是增加了一个int变量,只是是0,看不出来,输出大小为24(4(虚表)+16+4(int))
虚表的个数增加了一个,就是新的虚函数isEatFish, isCanSay被CCat新增的虚函数替换了,
再看下复杂点的
代码:
class CAnimal { public: CAnimal() { strncpy(m_szName, "Test!", sizeof(m_szName)); } virtual ~CAnimal() { _asm NOP } virtual bool isCanSay(){return false;} static bool isCanFly(){return false;} public: char m_szName[16]; static int m_snType; }; int CAnimal::m_snType = 0x12345678; class CCat : virtual public CAnimal { public: CCat(){m_nCatTest = 0x123;} //virtual bool isCanSay(){return true;} virtual bool isEatFish(){return true;} int m_nCatTest; }; class CCDog: virtual public CAnimal { public: CCDog(){m_nDogTest = 0x789;} virtual ~CCDog(){} virtual bool isEatMeat(){return true;} //virtual bool isCanSay(){return true;} int m_nDogTest; }; class CPet : public CCat, public CCDog { public: virtual bool isCanDomestication(){return true;} }; int _tmain(int argc, _TCHAR* argv[]) { CPet CpetObj; CpetObj.isEatFish(); CpetObj.isEatMeat(); CpetObj.isCanSay(); return 0; }
[code]
CPet CpetObj;
012F24B1 push 1
012F24B3 lea ecx,[ebp-84h]
012F24B9 call CPet::CPet (12F1203h)
012F24BE mov byte ptr [ebp-4],2
CpetObj.isEatFish();
012F24C2 lea ecx,[ebp-84h]
012F24C8 call CCat::isEatFish (12F117Ch)
CpetObj.isEatMeat();
012F24CD lea ecx,[ebp-78h]
012F24D0 call CCDog::isEatMeat (12F1131h)
CpetObj.isCanSay();
012F24D5 mov eax,dword ptr [ebp-80h]
012F24D8 mov ecx,dword ptr [eax+4]
012F24DB lea ecx,[ebp+ecx-80h]
012F24DF call CAnimal::isCanSay (12F11C2h)
(图四)
虚表对应的跳转代码
代码:
0x12f89a4 012F117C jmp CCat::isEatFish (12F2880h) 012F110E jmp CPet::isCanDomestication (12F2D60h) 0x12f8998 012F1131 jmp CCDog::isEatMeat (12F29E0h) 0x12f8988 012F123A jmp CPet::`scalar deleting destructor' (12F2E00h) 012F11C2 jmp CAnimal::isCanSay (12F26E0h)
总结下:
C++内存分布状况非静态成员都在类内,静态成员在常量区,非virtual函数在在class不扯关系(不保存表关系),虚函数class内会有虚表指针指向虚表,虚表中保存虚函数的地址,在调用的时候通过间接取调用(要用new才能看到到)。
有同名同参的虚函数,继承改写,则派生类的虚表中base class的虚表会被覆盖,多继承就多几个虚表,成员对象规则符合一般规则。
菱形的复杂结构,等搞清楚。。。。
五边形 (2011年6月24日)