Recently, in order to thoroughly understand the object model of C ++, how does the compiler implement polymorphism and what is the virtual table?CodeTo implement and deduce the object model of C ++.
First, define the following inheritance system: source code
Then construct the Object Pointer. The Code is as follows:
Int _ tmain (INT argc, _ tchar * argv [])
{
Ivtbl * pvtbl = NULL;
// VT to real1
Int isize = sizeof (vtblreal1 );
Pvtbl = new vtblreal1 ();
Void (_ thiscall vtblreal1: * PFn) (void) = & vtblreal1: F2; // assign values to the member function pointer Declaration
// Void * ptemp = (void *) (& vtblreal1: F2 );
If (pvtbl! = NULL)
{
Pvtbl-> F1 ();
Pvtbl-> F2 ();
(Vtblreal1 *) pvtbl-> * PFn) (); // call the member function pointer
Delete pvtbl;
}
// VT to realb
Pvtbl = new vtblrealb ();
If (pvtbl! = NULL)
{
Pvtbl-> F1 ();
Delete pvtbl;
}
// VT to Diamond
// Ivtbl * pvtbl = new vtbldiamond (); // The conversion is not clear about vtblreal1 or vtblrealb ??? Tangle
Vtblreal1 * pvtblreal1 = new vtbldiamond ();
If (pvtblreal1! = NULL)
{
Pvtblreal1-> F1 ();
Vtblrealb * ptempvtbb = dynamic_cast <vtblrealb *> (pvtblreal1 );
Ptempvtbb-> F1 ();
Delete pvtblreal1;
}
Vtblrealb * pvtbrealb = new vtbldiamond ();
If (pvtbrealb! = NULL)
{
Ivtbl * pvtbl = dynamic_cast <ivtbl *> (pvtbrealb );
Pvtbl-> F1 ();
Delete pvtbrealb;
}
Return 0;
}
The execution result is as follows:
Let's perform disassembly and debugging to see how the compiler helps us implement it?
Let's look at the initialization of an object from the Assembly perspective.
// Vtbldemo. cpp: defines console applicationsProgram.
//
# Include "stdafx. H"
# Include "ivtbl. H"
# Include "vtblbase. H"
# Include "vtblreal1.h"
# Include "vtblrealb. H"
# Include "vtbldiamond. H"
Int _ tmain (INT argc, _ tchar * argv [])
{
00961690 push EBP
00961691 mov EBP, ESP
00961693 push 0 ffffffffh
00961695 push offset _ ehhandler $ _ wmain (965468 H)
0096169a mov eax, dword ptr fs: [00000000 H]
009616a0 push eax
009616a1 sub ESP, 1dch // apply for stack space
009616a7 push EBX
009616a8 push ESI
009616a9 push EDI
009616aa Lea EDI, [ebp-1E8h]
009616b0 mov ECx, 77 H
009616b5 mov eax, 0 cccccccch
009616ba rep STOs dword ptr es: [EDI]
009616bc mov eax, dword ptr [___ security_cookie (96a0a8h)]
009616c1 XOR eax, EBP
009616c3 push eax
009616c4 Lea eax, [ebp-0Ch]
009616c7 mov dword ptr fs: [00000000 H], eax
Ivtbl * pvtbl = NULL;
009616cd mov dword ptr [ebp-14h], 0 // initialize pvtbl pointer to null
// VT to real1
Int isize = sizeof (vtblreal1 );
009616d4 mov dword ptr [ebp-20h], 0ch // assign sizeof (vtblreal1) to the ebp-20h, that is, the isize local variable)
//? Why = 0ch = (sizeof (vftable) + sizeof (DWORD m_dwvalue) + sizeof (m_baseval) = 12
Pvtbl = new vtblreal1 ();
009616db push 0ch // sizeof (vtblreal1)
009616dd call operator new (9611e0h) // internally call malloc to apply for heap Space
009616e2 add ESP, 4
009616e5 mov dword ptr [ebp-140h], eax // assign the applied memory address to the ebp-140h (pvtbl pointer)
009616eb mov dword ptr [ebp-4], 0
009616f2 cmp dword ptr [ebp-140h], 0 // compare whether it is null, if it is null, jump to 96170eh
009616f9 je wmain + 7eh (96170eh)
009616fb mov ECx, dword ptr [ebp-140h] // assign the applied memory address to ECx
00961701 call vtblreal1: vtblreal1 (961_ch) // call the vtblreal1 constructor ?? What do constructors do? See the following detailed descriptions of vtblreal1 constructor:
00961706 mov dword ptr [ebp-1E4h], eax // assign the return value to the ebp-1e4h ?? Ebp-140h to view the following object initialization Research
0096170c JMP wmain + 88 h (961718 H)
0096170e mov dword ptr [ebp-1E4h], 0
00961718 mov eax, dword ptr [ebp-1E4h]
0096171e mov dword ptr [ebp-14Ch], eax
00961724 mov dword ptr [ebp-4], 0 ffffffffh
0096172b mov ECx, dword ptr [ebp-14Ch]
00961731 mov dword ptr [ebp-14h], ECx
Void (_ thiscall vtblreal1: * PFn) (void) = & vtblreal1: F2; // assign values to the member function pointer Declaration
00961734 mov dword ptr [ebp-2Ch], offset vtblreal1: 'vcall' {4} '(9611feh) // vtblreal1: 'vcall' {4 }':
// 00da11fe JMP vtblreal1: 'vcall' {4} '(0da1c80h)
// 00da1c80 mov eax, dword ptr [ECx]
// 00da1c82 jmp dword ptr [eax + 4]
// Jump to ECx + 4 vtblreal1: F2 () Address
// Void * ptemp = (void *) (& vtblreal1: F2 );
If (pvtbl! = NULL)
0096173b cmp dword ptr [ebp-14h], 0
0096173f je wmain + 128 H (9617b8h)
{
Pvtbl-> F1 ();
00961741 mov eax, dword ptr [ebp-14h]
00961744 mov edX, dword ptr [eax]
00961746 mov ESI, ESP
00961748 mov ECx, dword ptr [ebp-14h]
0096174b mov eax, dword ptr [edX] // The first function address of the virtual table corresponds to the F1 Function
0096174d call eax // call F1
0096174f cmp esi, ESP
00961751 call @ ILT + 400 (_ rtc_checkesp) (961195 H)
Pvtbl-> F2 ();
00961756 mov eax, dword ptr [ebp-14h]
00961759 mov edX, dword ptr [eax]
0096175b mov ESI, ESP
0096175d mov ECx, dword ptr [ebp-14h]
00961760 mov eax, dword ptr [edX + 4] // virtual table address + 4 second function address F2 Function
00961763 call eax // call F2
00961765 cmp esi, ESP
00961767 call @ ILT + 400 (_ rtc_checkesp) (961195 H)
(Vtblreal1 *) pvtbl-> * PFn) (); // call the member function pointer
0096176c mov ESI, ESP
0096176e mov ECx, dword ptr [ebp-14h]
00961771 call dword ptr [ebp-2Ch] // This-> F2 (), ECx is the this pointer
00961774 cmp esi, ESP
00961776 call @ ILT + 400 (_ rtc_checkesp) (961195 H)
Delete pvtbl;
0096177b mov eax, dword ptr [ebp-14h]
0096177e mov dword ptr [ebp-128h], eax
00961784 mov ECx, dword ptr [ebp-128h]
0096178a mov dword ptr [ebp-134h], ECx
00961790 cmp dword ptr [ebp-134h], 0
00961797 je wmain + 11eh (9617aeh)
00961799 Push 1
0096179b mov ECx, dword ptr [ebp-134h]
009617a1 call ivtbl: 'scalar deleting destructor' (961064 h) // call the destructor to release resources
009617a6 mov dword ptr [ebp-1E4h], eax
009617ac JMP wmain + 128 H (9617b8h)
009617ae mov dword ptr [ebp-1E4h], 0
}
......
// VT to Diamond
// Ivtbl * pvtbl = new vtbldiamond (); // The conversion is not clear about vtblreal1 or vtblrealb ??? Tangle
Vtblreal1 * pvtblreal1 = new vtbldiamond ();
......
Vtblrealb * pvtbrealb = new vtbldiamond ();
00961950 push 38 H
00961952 call operator new (9611e0h)
00961957 add ESP, 4
0096195a mov dword ptr [ebp-170h], eax
00961960 mov dword ptr [ebp-4], 3
00961967 cmp dword ptr [ebp-170h], 0
0096196e je wmain + 2f3h (961983 H)
00961970 mov ECx, dword ptr [ebp-170h]
00961976 call vtbldiamond: vtbldiamond (961258 H)
0096197b mov dword ptr [ebp-1E4h], eax
00961981 JMP wmain + 2fdh (96198dh)
00961983 mov dword ptr [ebp-1E4h], 0
0096198d mov eax, dword ptr [ebp-1E4h]
00961993 mov dword ptr [ebp-17Ch], eax
00961999 mov dword ptr [ebp-4], 0 ffffffffh
009619a0 cmp dword ptr [ebp-17Ch], 0
009619a7 je wmain + 32ah (9619bah)
009619a9 mov ECx, dword ptr [ebp-17Ch]
009619af add ECx, 0ch
009619b2 mov dword ptr [ebp-1E8h], ECx
009619b8 JMP wmain + 334 H (9619c4h)
009619ba mov dword ptr [ebp-1E8h], 0
009619c4 mov edX, dword ptr [ebp-1E8h]
009619ca mov dword ptr [ebp-50h], EDX
If (pvtbrealb! = NULL)
009619cd cmp dword ptr [ebp-50h], 0
009619d1 je wmain + 39bh (961a2bh)
{
Ivtbl * pvtbl = dynamic_cast <ivtbl *> (pvtbrealb); // convert vtbldiamond type pointer to ivtbl pointer...
009619d3 mov eax, dword ptr [ebp-50h]
009619d6 mov dword ptr [pvtbl], eax
Pvtbl-> F1 ();
009619d9 mov eax, dword ptr [pvtbl]
009619dc mov edX, dword ptr [eax]
009619de mov ESI, ESP
009619e0 mov ECx, dword ptr [pvtbl]
009619e3 mov eax, dword ptr [edX]
009619e5 call eax
009619e7 cmp esi, ESP
009619e9 call @ ILT + 400 (_ rtc_checkesp) (961195 H)
Delete pvtbrealb;
009619ee mov eax, dword ptr [ebp-50h]
009619f1 mov dword ptr [ebp-158h], eax
009619f7 mov ECx, dword ptr [ebp-158h]
009619fd mov dword ptr [ebp-164h], ECx
00961a03 cmp dword ptr [ebp-164h], 0
00961a0a je wmain + 391 H (961a21h)
00961a0c Push 1
00961a0e mov ECx, dword ptr [ebp-164h]
00961a14 call vtblrealb: 'scalar deleting destructor' (961091 h) // see the following object analysis process
// The difference between declaring virtual destructor and declaring virtual destructor is not found in this experiment.
// If the virtual declarative destructor is not used in my environment, the VC debugger prompts that the assertions fail.
...
Object initialization Research
EBP 0x002ffb10 009616db push 0ch 009616dd call operator new (9611e0h) 009616e2 add ESP, 4 009616e5 mov dword ptr [ebp-140h], eax Ebp-140h eax (pointer returned by operator new) 00961701 call vtblreal1: vtblreal1 (961_ch) 00961706 mov dword ptr [ebp-1E4h], eax Ebp-1E4h eax (constructors return content) EBP = 002ffb10 EBP-140H = 0x002ff9d0 The EBP-1E4H = 0x002ff92c Let's see what both of them store? 0x002ff9d0 004b4f58 cccccccccc cccccccc 0x002ff92c 004b4f58 cccccccccc cccccccc // Point to the same address? What is stored in it? 0x004b4f58 00967818 00000000 00000002 fdfdfdfd ababababab abababab 00000000 The address value does not have an initialized data region ....? Let's take a look. 0x00967818 009610d2 0096100a 00000000 009688a8 00961028 0096115e 00000000 009688c0 009610f0 00000000 The address seems to be an array ?? Vtblreal1: F1: 009610d2 JMP vtblreal1: F1 (961de0h) Vtblreal1: F2: 0096100a JMP vtblreal1: F2 (961d70h) Well, it turns out to be the function address of vtblreal1, so 0x00967818 is the virtual table address pointing to the vtblreal1 object. The subsequent 00000000 and 00000002 represent vtblbase: m_baseval (0) and vtblreal1: m_dwvalue (2), respectively) So we can draw a conclusion. The new object and the structure returned by initialization both point to the vtblbase object address. Let's start over. So what are the new and constructor doing here? New initialization address memory is cdcdcdcd obviously not initialized After the constructor is called 0x003f4f58 00da7818 00000000 00000002 Corresponding to the virtual table and member variables, it is certain that the constructor has completed object initialization. |
Research on object structure Process
Vtblrealb: 'scalar deleting destructor ': 00da1091 JMP vtblrealb: 'scalar deleting destructor' (0da1c10h) Vtblrealb: 'scalar deleting destructor ': 00da1c10 push EBP // address provided by VC 00da1c11 mov EBP, ESP 00da1c13 sub ESP, 0cch 00da1c19 push EBX 00da1c1a push ESI 00da1c1b push EDI 00da1c1c push ECx 00da1c1d Lea EDI, [ebp-0CCh] 00da1c23 mov ECx, 33 H 00da1c28 mov eax, 0 cccccccch 00da1c2d rep STOs dword ptr es: [EDI] 00da1c2f pop ECx 00da1c30 mov dword ptr [ebp-8], ECx 00da1c33 mov ECx, dword ptr [this] 00da1c36 call vtblrealb ::~ Vtblrealb (0da1_1 h) // call the vtblrealb destructor 00da1c3b mov eax, dword ptr [EBP + 8] 00da1c3e and eax, 1 00da1c41 je vtblrealb: 'scalar deleting destructor' + 3fh (0da1c4fh) 00da1c43 mov eax, dword ptr [this] // assign this pointer to the eax register 00da1c46 push eax 00da1c47 call operator Delete (0da10b4h) // call the delete function to delete 00da1c4c add ESP, 4 // stack balance 00da1c4f mov eax, dword ptr [this] 00da1c52 pop EDI 00da1c53 pop ESI 00da1c54 pop EBX 00da1c55 add ESP, 0cch 00da1c5b cmp ebp, ESP 00da1c5d call @ ILT + 400 (_ rtc_checkesp) (0da1195h) 00da1c62 mov ESP, EBP 00da1c64 pop EBP 00da1c65 RET 4 |
In this way, we can clearly observe how the compiler helps us to control the entire lifecycle of an object.