C + + class because of the existence of inheritance is more complex than the C-era struct, especially when the virtual function, as well as the multi-inheritance, and so on, so that its memory layout becomes unrecognizable. I can not grasp the truth, I just on a practical platform to do some exploration, and use this note to record my exploration results.
Although there are some things in the C + + standard that do not stipulate how to do, different implementations may have different approaches, but understanding how a real system is done will also benefit us in a more in-depth understanding of C + + or extrapolate to understand other implementations, and if we understand the specific implementation of our own systems , they can do whatever they like.
No virtual function order inherits memory layout
In the case of single inheritance, the memory layout of the class object is also not complex. In the case where Class B inherits Class A, we can roughly guess the memory distribution of the object B, which is to store the members of Class A first, then the members of Class B itself. Because such a layout does not need to do any processing to convert the parent class pointer, it is compatible with C, and the implementation of this object model is relatively simple and reasonable. But how exactly is it put? There are about 4 things that can be thought of. Like Class b:a{...} This single-inheritance relationship, the default inheritance is private inheritance, the access adornment does not affect the memory layout, 4 kinds of memory layout may be:
1. Store A as a whole block of structure, and then store B as a whole block of structure, that is, a bit like this class Temp{a x; B y;} This equivalent layout;
2. First store A as a whole piece of structure, and then in turn emit each member variable of Class B itself, like class Temp{a x; The various member variables of B ...} This equivalent layout;
3. First, the individual member variables of a are discharged sequentially, then B is stored as a whole structure, just like the individual members of class temp{a. B y} this equivalent layout;
4. First emit the individual member variables of a and then the various member variables that emit B, that is, the individual members of class Temp{a. All members of B ....;} ;
What is the difference between these 4 layouts? Of course there are ~ because of the existence of byte alignment! The meaning of these layouts is obviously different, of course, if the byte pair alignment is 1 byte aligned, these layout models appear to be the same on the surface.
If you are familiar with the structure of the byte alignment, it is easy to find some suitable classes to overthrow them one by one, if not, it is estimated that the process of finding suitable classes will be more confusing. Of course, do not deliberately to find some very clever class, can prove right and wrong actually can.
#pragma pack (8) class A1{public: double B1; Char C1; A1 (): B1 (0), C1 (0xFF) {}}; Classa2:a1{public: Char A2; int A3; A2 (): A2 (0xEE), A3 (0x22222222) {}};
Like the two classes above, the rules of byte alignment can overturn at least two memory layouts in the 4 possible distributions. According to the byte alignment rules, layout types 1 and 2 are equivalent layouts in the above two classes, while the 3rd and 4th may have different layout conditions. In the case of the IDE we can easily write code and then directly grab the data, I use VS2013 here. The code you tested together has some of the following, along with the above:
#include "stdafx.h" #pragma pack (8) class a1{public:double B1; Char C1; A1 (): B1 (0), C1 (0xFF) {}}; Class A2:a1{public:char A2; int A3; A2 (): A2 (0xEE), A3 (0x22222222) {}}; Class A2_{public://A2 equivalent class char A2 without A1 inheritance condition; int A3; A2_ (): A2 (0xEE), A3 (0x22222222) {}}; Class Temp1{public://classes with Case 1 equivalent layout A1 A1; A2_ A2;}; Class Temp2{public://classes with Case 2 equivalent layout A1 A1; Char A2; int A3; Temp2 (): A2 (0xEE), A3 (0x22222222) {}}; Class Temp3{public://Classes Double B1 with case 3 equivalent layout; Char C1; A2_ A2; Temp3 (): B1 (0), C1 (0xFF) {}}; Class Temp4{public://Classes Double B1 with case 4 equivalent layout; Char C1; Char A2; int A3; Temp4 (): B1 (0), C1 (0xFF), A2 (0xEE), A3 (0x22222222) {}}; int res; In the main function break point grab memory data int _tmain (int argc, _tchar* argv[]) {A2 x; res = sizeof (x); Temp1 y; res = sizeof (y); Temp2 Z; res = sizeof (z); Temp3 m; res = sizeof (m); Temp4 N; res = sizeof (n); return 0;}
The above code in the VS2013 inside the layout grab package as follows:
A2 inherit from A1, here is the A2 class in memory layout, let's see
Figure 1:a2 in-memory layout
Next we simulate the A2 memory layout in 4 ways to see which is the truth.
1. Scenario 1: A1 (the base class of A2) is stored as an entire block, and then the a2_ (A2 own member variable) is stored as a whole block of structure, that is, a bit like this class TEMP1{A1 x; A2_ y;} This equivalent layout, we simulate the memory layout of the A2 in this way, and find that its memory layout is determined and the same as Figure 1 (A2 memory layout)
Figure 2
2. Scenario 2: A1 (the base class of A2) is stored as a whole block of structure, and then discharged the class A2 its own member variable members, like class TEMP2{A1 x; A2 the various member variables ...} This equivalent layout, we simulate the memory layout of the A2 in this way, and find that its memory layout is determined and the same as Figure 1 (A2 memory layout)
Figure 3
3. Case 3: The individual member variables of the A1 (base class of A2) are discharged sequentially, and then the a2_ (A2 own member variable) is stored as a whole structure, just like the individual members of class temp3{a. B y} This equivalent layout, we simulated the A2 memory layout in this way, and found that the figure 1 (A2 memory layout) is not the same, stating that situation 3 is wrong.
Figure 4
4. Case 4: The individual member variables of the A1 (base class of A2) are discharged and then the individual member variables of the A2 own member variable are discharged, that is, the individual members of class temp4{a1 such as this ...; Various members of A2 ...}, we simulate the A2 memory layout in this way, and find that the figure 1 (A2 memory layout) is different, stating that scenario 4 is wrong.
Figure 5
Obviously, the pattern of memory distribution in the 3rd and 4th cases has been overturned, and one of the 1th and 2nd cases is correct, and to further overturn one of them has to change the type of membership in our class. My idea is this: make the widest member of the base class A1 less than 8 bytes, and let sizeof (A1) not be an integer multiple of 8, and then make the widest member of Class A2 into a 8-byte double.
#include "stdafx.h" #pragma pack (8) class A1{public: Short B1; Char C1; A1 (): B1 (0XAAAA), C1 (0xFF) {}}; Class A2:a1{public: char A2; Double A3; A2 (): A2 (0xEE), A3 (0) {}}; Class A2_{public: //A2 equivalent class char A2 without A1 inheritance condition ; Double A3; A2_ (): A2 (0xEE), A3 (0) {}}; Class Temp1{public: //classes with Case 1 equivalent layout A1 A1; A2_ A2;}; Class Temp2{public: //classes with Case 2 equivalent layout A1 A1; Char A2; Double A3; Temp2 (): A2 (0xEE), A3 (0) {}}; int res; In the main function break point grab memory data int _tmain (int argc, _tchar* argv[]) { A2 x; res = sizeof (x); Temp1 y; res = sizeof (y); Temp2 Z; res = sizeof (z); return 0;}
The test results are as follows:
A2 inherit from A1, here is the A2 class in memory layout, let's see
Figure 6:A2 Memory layout
Next we simulate the A2 memory layout in 2 ways to see which is the truth.
1. Scenario 1: A1 (the base class of A2) is stored as an entire block, and then the a2_ (A2 own member variable) is stored as a whole block of structure, that is, a bit like this class TEMP1{A1 x; A2_ y;} This equivalent layout, we simulated the A2 memory layout in this way, found that the size of the Temp1 is 24 bytes, which is inconsistent with the size of A2 (see Figure 6, 16 bytes), all cases 1 failed.
Figure 7
2. Scenario 2: A1 (the base class of A2) is stored as a whole block of structure, and then discharged the class A2 its own member variable members, like class TEMP2{A1 x; A2 the various member variables ...} This equivalent layout, we simulate the memory layout of the A2 in this way, and find that its memory layout is determined and the same as Figure 6 (A2 memory layout)
Now we can conclude that the memory layout of a single-inherited class is equivalent to the second case: the base is stored as an entire block, and then the various member variables of the class derived itself are emitted in turn, like class Temp{base x; Derived the various member variables ...} This equivalent layout.
In fact, this is also the most reasonable situation, because the C + + standard provisions need to guarantee the integrity of the base class sub-object, so the base class must be stored as a complete structure.
Knowing the memory layout of these objects, we can take any single inheriting class to grasp its inside memory, and always recursively reasoning down to get the final memory layout. Single inheritance or relatively simple, there is a more special case is to inherit the empty class, the empty class this thing each system should have a different implementation, on the implementation of Windows I tested a bit, in addition to some basic things better estimate, Many other situations seem to find no good reason to explain it, it is more difficult to summarize, so, simply I do not say empty class this situation, no meaning.
Multiple Inheritance Cases
Since the case of single inheritance is already clear, the case of multiple inheritance can naturally be inferred, naturally, according to the Order of declaration of succession, the class is discharged sequentially, and finally to the members of the derived class itself. For example, Class B inherits A1, A2, A3, which is class b:a1,a2,a3{...}; Its memory layout is equivalent to class TEMP{A1 A1; A2 A2; A3 A3; ... }; The argument process and the time of the single inheritance is almost, it is not difficult to compare more things just, here I will not put out, casually put an example of it.
#include "stdafx.h" #pragma pack (8) class A1{public: int A1; A1 (): A1 (0XA1A1A1A1) {}};class a2{public: int A2; A2 (): A2 (0xa2a2a2a2) {}};class a3{public: int A3; A3 (): A3 (0XA3A3A3A3) {}};class b:a1,a2, A3 {public: int B; B (): B (0xbbbbbbbb) {}}; int _tmain (int argc, _tchar* argv[]) { B bb;
Single-Inheritance memory model of C + + no virtual function