Explore the C ++ Object Model

Source: Internet
Author: User


What exactly does a class object instance contain?

The sample code is very simple:
# Include <iostream>

Using namespace std;

Class
{
Public:
Void fun1 () {cout <"fun1 ";}
Virtual void fun2 () {cout <"fun2 ";}
Virtual ~ A (){}

Char m_cA;
Int m_nA;
Static int s_nCount;
};

Int A: s_nCount = 0;

Int main ()
{
A * p = new;
P-> fun2 ();

System ("pause ");

Return 0;
}
In the main function, we set the breakpoint in the place of system ("pause");, and then let the program run here.

Enter the WinDbg command ?? Sizeof (* p) asks him to print the size of object A. The output is as follows:
0: 000> ?? Sizeof (* p)
Unsigned int 0xc
We can see that the object size of instance A is 0xc = 12 bytes.

Enter the WinDbg command dt p to print the memory layout of the object referred to by p. The output is as follows:
0: 000> dt p
Local var @ 0x13ff74 Type *
Zero x 00034600
+ 0x000 _ VFN_table: 0x004161d8
+ 0x004 m_cA: 120 'X'
+ 0x008 m_nA: 0n0
= 0041c3c0 A: s_nCount: 0n0
We can see that the object instance of A is composed of virtual table pointers, m_cA and m_nA, exactly 12 bytes (internal char is 4-byte aligned ).

The address of the last static variable s_nCount is 0041c3c0. We can use the command! Address 0041c3c0: view the attributes of the address. The result is as follows:
0: 000>! Address 0041c3c0
Usage: Image
Allocation Base: 00400000
Base Address: 0041b000
End Address: 0041f000
Region Size: 00004000
Type: 01000000 MEM_IMAGE
Status: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
More info: lmv m ConsoleTest
More info :! LMIS ConsoleTest
More info: ln 0x41c3c0
You can see the readable and writable data section (. data) of the class static variables compiled in consoletest.exe executable file)

Conclusion: In C ++, class instance objects are composed of virtual table pointers and member variables (generally the first four bytes are virtual table pointers), and class static variables are distributed in PE files. in the data section, it has nothing to do with class instance objects.


Virtual table location and content

Based on + 0x000 _ VFN_table: 0x004161d8, continue the above debugging. We can see that the virtual table address is at 0x004161d8, input! Address 0x004161d8: view the properties of the virtual table address:
0: 000>! Address 0x004161d8
Usage: Image
Allocation Base: 00400000
Base Address: 00416000
End Address: 0041b000
Region Size: 00005000
Type: 01000000 MEM_IMAGE
Status: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
More info: lmv m ConsoleTest
More info :! LMIS ConsoleTest
More info: ln 0x4161d8
You can see the read-only data section (. rdata) where the table is compiled and translated into the executable file consoletest.exe)

Next, let's take a look at what is in the virtual table. Input dps 0x004161d8 to view the symbol of the address of the virtual table. The output is as follows:
0: 000> dps 0x004161d8
004161d8 00401080 ConsoleTest! A: fun2 [f: \ test \ consoletest. cpp @ 13]
004161dc 004010a0 ConsoleTest! A: 'scalar deleting destructor'
004161e0 326e7566
004161e4 00000000
We can see that the virtual table exactly contains our two virtual functions fun2 () and ~ A ().

In addition, we can try several new A instances. We can see that their virtual table addresses are 0x004161d8.

Conclusion: In C ++, the virtual table content is composed of virtual function addresses. The virtual table is distributed in the. rdata section of the PE file, and all instances of the same class share the same virtual table.


Disable the generation of virtual tables

We can use _ declspec (novtable) to tell the compiler not to generate virtual tables. This technology is widely used in ATL to reduce the memory overhead of virtual tables. Our original code is changed
Class _ declspec (novtable)
{
Public:
Void fun1 () {cout <"fun1 ";}
Virtual void fun2 () {cout <"fun2 ";}
Virtual ~ A (){}

Char m_cA;
Int m_nA;
Static int s_nCount;
};
After debugging the original method, we will see the program Crash as soon as it runs to p-> fun2 (). What is the reason?
Use the original ?? Sizeof (* p) command, you can see that the object size is still 12 bytes, input dt p, output:
0: 000> dt p
Local var @ 0x13ff74 Type *
0x00033e58
+ 0x000 _ VFN_table: 0x00030328
+ 0x004 m_cA: 40 '('
+ 0x008 m_nA: 0n0
= 0040dce0 A: s_nCount: 0n0
From the above we can see that the virtual table still exists, but then input dps 0x00030328 to view the virtual table content, you will find that the virtual table content does not exist now:
0: 000> dps 0x00030328
00030328 00030328
0003032c 00030328
00030330 00030330
However, our program still calls the virtual function fun2 through the virtual table. No wonder it will Crash.

Conclusion: Through _ declspec (novtable), we can only disable the compiler from generating virtual tables, but cannot prevent objects from still containing virtual table pointers (the object size cannot be reduced ), it cannot prevent the program from accessing the virtual table (although the actual virtual table does not exist). Therefore, it is only applicable to classes that will never be instantiated (base classes)


Memory Model of a single inherited object

Next we will simply change the above Code to let B inherit A and rewrite the original virtual function fun2:
# Include <iostream>

Using namespace std;

Class
{
Public:
Void fun1 () {cout <"fun1 ";}
Virtual void fun2 () {cout <"fun2 ";}
Virtual ~ A (){}

Char m_cA;
Int m_nA;
Static int s_nCount;
};

Int A: s_nCount = 0;

Class B: public
{
Public:
Virtual void fun2 () {cout <"fun2 in B ";}
Virtual void fun3 () {cout <"fun3 in B ";}

Public:
Int m_nB;
};

Int main ()
{
B * p = new B;
A * p1 = p;

P1-> fun2 ();

System ("pause ");

Return 0;
}
Debug with the original method to view the memory layout of object B
0: 000> dt p
Local var @ 0x13ff74 Type B *
Zero x 00034640
+ 0x000 _ VFN_table: 0x004161d8
+ 0x004 m_cA: 120 'X'
+ 0x008 m_nA: 0n0
= 0041c3e0 A: s_nCount: 0n0
+ 0x00c m_nB: 0n0
We can see that the size of object B is the size of object A plus 4 (m_nB), which is 16 bytes in total. The contents of the virtual table of object B are as follows:
0: 000> dps 0x004161d8
004161d8 00401080 ConsoleTest! B: fun2 [f: \ test \ consoletest. cpp @ 26]
004161dc 004010c0 ConsoleTest! B: 'scalar deleting destructor'
004161e0 004010a0 ConsoleTest! B: fun3 [f: \ test \ consoletest. cpp @ 27]
004161e4 326e7566
We can see that the virtual table stores all the virtual function addresses of B: fun2 (),~ B (), fun3 ()

Conclusion: when a single inheritance operation is performed, the parent class and the Child class share the same virtual table pointer. After the Child class data is added to the parent class data, the object pointer of the parent class and the Child class remains the same during mutual conversion.


Memory Model of multiple inherited objects

We changed the above Code to A multi-Inheritance Method, class A, class B, and then C inherits A and B:
# Include <iostream>
Using namespace std;
Class
{
Public:
Virtual void fun () {cout <"fun in ";}
Virtual void funA () {cout <"funA ";}
Virtual ~ A (){}
Char m_cA;
Int m_nA;
Static int s_nCount;
};
Int A: s_nCount = 0;
Class B
{
Public:
Virtual void fun () {cout <"fun in B ";}
Virtual void funB () {cout <"funB ";}
Int m_nB;
};
Class C: public A, public B
{
Public:
Virtual void fun () {cout <"fun in C ";};
Virtual void funC () {cout <"funC ";}
Int m_nC;
};
Int main ()
{
C * p = new C;
B * p1 = p;
P-> fun ();
System ("pause ");
Return 0;
}
Still use the original debugging method to view the memory layout of C
0: 000> dt p
Local var @ 0x13ff74 Type C *
Zero x 00034600
+ 0x000 _ VFN_table: 0x004161e4
+ 0x004 m_cA: 120 'X'
+ 0x008 m_nA: 0n0
= 0041c3e0 A: s_nCount: 0n0
+ 0x00c _ VFN_table: 0x004161d8
+ 0x010 m_nB: 0n0
+ 0x014 m_nC: 0n0
We can see that the C object consists of 0x18 = 24 bytes. The data in turn is the virtual table pointer, the data in A, the virtual table pointer, the data in B, and the data in C.

View the content of the first virtual table:
0: 000> dps 0x004161e4
004161e4 004010f0 ConsoleTest! C: fun [f: \ test \ consoletest. cpp @ 36]
004161e8 004010b0 ConsoleTest! A: funA [f: \ test \ consoletest. cpp @ 13]
004161ec 00401130 ConsoleTest! C: 'scalar deleting destructor'
004161f0 00401110 ConsoleTest! C: funC [f: \ test \ consoletest. cpp @ 37]
004161f4 416e7566
We can see that the first three virtual functions of the preceding virtual table meet the virtual table requirements of A, and finally add the new virtual function funC of C. Therefore, the virtual table meets the requirements of both A and C, that is to say, A and C share the same virtual table pointer.

Let's look at the second virtual table:
0: 000> dps 0x004161d8
004161d8 00401560 ConsoleTest! [Thunk]: C: fun 'adjustor {12 }'
004161dc 004010d0 ConsoleTest! B: funB [f: \ test \ consoletest. cpp @ 28]
We can see that the second virtual table meets the virtual table requirements of B, and rewrite the virtual function fun of B with C, so it is used for B.

Let's look at the layout of base class object B:
0: 000> dt p1
Local var @ 0x13ff70 Type B *
0x003e460c
+ 0x000 _ VFN_table: 0x004161d8
+ 0x004 m_nB: 0n0
We can see that the address of the p1 pointer on the stack is 0x13ff70, and the address of the object that p1 points to is 0x003e460c. So after converting the C pointer to the B pointer, the difference between B's address and C's address is 0x003e460c-0x00034600 = 0xc = 12 bytes. That is to say, B's pointer p1 points to our second virtual table pointer above.

Conclusion: when multiple inheritance occurs, the derived class shares a virtual table pointer with the first base class, and their object pointers do not change their conversion values. Other base classes (not the First Class) and the object pointer of the derived class have a certain offset when converting each other.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.