Source program in c ++:
Copy codeThe Code is as follows: class X {
Private:
Int I;
};
Int main (){
X x;
}
The preceding class X does not define the constructor and only has an int I.
The assembler is as follows:
Copy codeCode:; 7: int main (){
Push ebp; ebp is a register that always points to the stack bottom of a function call stack. As the base address, the offset is used to access the variables on the call stack, but no variables need to be accessed here, so it does not work
Mov ebp and esp; these two sentences are used to save the value of the base ebp of the stack before calling main, and direct the ebp to the bottom of the stack of the main call stack.
Push ecx; roll back the value of the Register ecx, and move the top pointer of the stack esp 4 bytes forward
This statement reserves 4 bytes of space for the object to be created and writes the ecx value to it.
; 8: X x;
; 9 :}
Xor eax, eax; eax is also a register, which does not work here
Mov esp and ebp; move the top pointer of the stack to the position before pushing ecx, which releases the space of 4 bytes.
Pop ebp; restore the base address to the status before calling main
Ret 0; function return
Through assembly, it is found that by pushing ecx, the compiler moves 4 bytes at the top of the stack and writes the ecx value of the Register. Class X contains only one int, And the size is exactly 4 bytes, therefore, this sentence can be viewed as allocating space for object x. However, there is no function call to initialize this area. Therefore, there will be no initialization operations when a constructor is not clearly defined.
Next, let's look at a piece of c ++ program:
Copy codeThe Code is as follows: class X {
Private:
Int I;
Int j; // Add a member variable int j.
};
Int main (){
X x;
}
Compared with the above, a member variable int j is added to Class X, and the class size is changed to 8 bytes.
The corresponding assembly code is as follows:
Copy codeThe Code is as follows:; 8: int main (){
Push ebp
Mov ebp, esp
Sub esp, 8; the top pointer of the stack moves 8 bytes, which is exactly the size of Class X
; 9: X x;
; 10 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
From the assembly code, we can see that through sub esp and 8 commands, the stack indeed sets aside 8 bytes of space, which is exactly equal to the size of Class X and does not call any function for initialization.
Therefore, to sum up, when a class does not clearly define the constructor, the compiler does not have any function calls for initialization, but only sets aside the space required for objects at the top of the mobile stack. That is to say, in this case, the compiler does not provide the default constructor at all.
So what is the story about the default constructor provided by the compiler?
The first case is as follows:
C ++ source code is as follows:
Copy codeThe Code is as follows: class X {
Private:
Int I;
Int j; // Add a member variable int j.
Public:
Virtual ~ X (){
}
};
Int main (){
X x;
}
The Destructor are virtual functions.
The following is the assembly code corresponding to the main function:
Copy codeThe Code is as follows:; 13: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserve 12 bytes of space for object x. member variables int I and int j occupy 8 bytes. Due to virtual functions, vptr pointers occupy 4 bytes.
; 14: X x;
Lea ecx, dword ptr _ x $ [ebp]; obtains the first address of the x object and stores it in the ecx register.
Call ?? 0X @ QAE @ XZ; the constructor of x is called here.
; 15 :}
Lea ecx, dword ptr _ x $ [ebp]; get the first address of object x
Call ?? 1X @ UAE @ XZ; call the destructor
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
We can see that the constructor of object x has been called, and the compiler has indeed merged the default constructor.
The following is the constructor assembly code:
Copy codeThe Code is as follows :?? 0X @ QAE @ xz proc; X: X, COMDAT
; _ This $ = ecx
Push ebp
Mov ebp, esp
Push ecx
Mov dword ptr _ this $ [ebp], ecx; the ecx register contains the first address of object x.
Mov eax, dword ptr _ this $ [ebp]; first address of object x to register eax
Mov dword ptr [eax], OFFSET ?? _ 7X @ 6B @; set the value of the vptr pointer to vtable (OFFSET ?? _ 7X @ 6B @ is the address for obtaining the vtable)
And this statement proves that the vptr pointer is located at the actual address of the object.
Mov eax, dword ptr _ this $ [ebp]
Mov esp, ebp
Pop ebp
Ret 0
As you can see, because there are virtual functions that involve polymorphism, the constructor initializes the vptr pointer, but does not assign values to the other two variables int I and int j.
As can be seen from the above, when the class contains virtual functions, the compiler will indeed provide us with a default constructor without clearly defining the constructor. Therefore, when a class inherits from a virtual base class, it also meets the above situation.
In the second case, class Y inherits from Class X, which clearly defines a default constructor (not provided by the compiler), while class Y does not define any constructor:
Let's take a look at the c ++ source code:
Copy codeThe Code is as follows: class X {
Private:
Int I;
Int j;
Public:
X () {// X Display the default constructor defined
I = 0;
J = 1;
}
};
Class Y: public X {// Y inherits from X
Private:
Int I;
};
Int main (){
Y y;
}
No constructor is defined in class Y.
The following is the assembly code corresponding to the main function:
Copy codeCode:; 19: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserved 12 byte space for object y, member variable int I of y occupies 4 byte in parent class int I int j occupies 8 byte
; 20: Y y;
Lea ecx, dword ptr _ y $ [ebp]; get the first address of object y and store it in the register ecx
Call ?? 0Y @ QAE @ XZ; call the constructor of object y
; 21 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
The main function calls the default constructor of the default y object provided by the compiler.
The following is the assembly code of the default constructor of the y object provided by the compiler:
Copy codeThe Code is as follows :?? 0Y @ QAE @ xz proc; Y: Y, COMDAT
; _ This $ = ecx
Push ebp
Mov ebp, esp
Push ecx
Mov dword ptr _ this $ [ebp], ecx; ecx contains the first address of object y
Mov ecx, dword ptr _ this $ [ebp]
Call ?? 0X @ QAE @ XZ; call the constructor of the parent class X
Mov eax, dword ptr _ this $ [ebp]
Mov esp, ebp
Pop ebp
Ret 0
?? 0Y @ QAE @ XZ ENDP
We can see that the constructor of the y object calls the constructor of the parent class to initialize the member variables inherited from the parent class, but its member variables are still not initialized.
The following is the constructor assembly code of the parent class X:
Copy codeThe Code is as follows:; 7: X (){
Push ebp
Mov ebp, esp
Push ecx
Mov dword ptr _ this $ [ebp], ecx; ecx contains the first address of object y
; 8: I = 0;
Mov eax, dword ptr _ this $ [ebp]; object y first address to register eax
Mov dword ptr [eax], 0; initialize the variable I in the parent class
; 9: j = 1;
Mov ecx, dword ptr _ this $ [ebp]; the first address of object y to the Register ecx
Mov dword ptr [ecx + 4], 1; initialize the variable j in the parent class, in the memory space of object y, 8 bits starting from the first address are used to store the member variables inherited from the parent object, and the last 4 bytes are used to store their own member variables.
Because the first address stores the parent class member variable I, the memory address needs to move 4 bytes from the first address of object y to find the location of the parent class member variable j.
; 10 :}
Mov eax, dword ptr _ this $ [ebp]
Mov esp, ebp
Pop ebp
Ret 0
As you can see, the member variables of the y object inherited from the parent class are initialized by the parent class constructor. The parent object is included in the sub-object, and this pointer means that the first address stored in the register ecx is always the first address of sub-object y.
What if no constructor is defined in parent class X?
The following is the c ++ source code:
Copy codeThe Code is as follows: class X {
Private:
Int I;
Int j;
};
Class Y: public X {// Y inherits from X
Private:
Int I;
};
Int main (){
Y y;
}
Neither the parent class nor child class has any constructor.
The assembly code of the main function is as follows:
Copy codeCode:; 16: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserve 12 bytes for object y just like
; 17: Y y;
; 18 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
We can see that there is no function call in main, that is, the compiler does not provide the default constructor for sub-object y.
So what if the constructor with parameters in the parent class and no constructor In the subclass? At this time, the compiler will report an error.
In the third case, class Y contains member object X. The member object has a default constructor that is displayed and defined, while class Y does not have any constructor:
First look at the c ++ source code:
Copy codeCode:; 16: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserve 12 bytes for object y just like
; 17: Y y;
; 18 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
Class X is a member object of class Y.
The following is the assembly code of the main function:
Copy codeThe Code is as follows:; 21: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserved for object y 12 byte member object variables accounted for 8 byte object y itself accounted for 4 byte member object included in object y
; 22: Y y;
Lea ecx, dword ptr _ y $ [ebp]; the first address of object y is stored in ecx
Call ?? 0Y @ QAE @ XZ; calls the constructor of object y, which is the default constructor provided by the compiler.
; 23 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
The constructor of object y is called, that is, the compiler provides the default constructor.
Constructor assembly code of object y:
Copy codeThe Code is as follows :?? 0Y @ QAE @ xz proc; Y: Y, COMDAT
; _ This $ = ecx
Push ebp
Mov ebp, esp
Push ecx
Mov dword ptr _ this $ [ebp], ecx; ecx contains the first address of object y
Mov ecx, dword ptr _ this $ [ebp]
Add ecx, 4; add 4 because the initial address of object y stores its member variable I
Call ?? 0X @ QAE @ XZ; call the constructor of member object x
Mov eax, dword ptr _ this $ [ebp]
Mov esp, ebp
Pop ebp
Ret 0
The constructor of object y calls the constructor of object x to initialize the member variables in the member object. The member variables of object y are not initialized.
Constructor assembly code of member object x:
Copy codeThe Code is as follows :?? 0X @ QAE @ xz proc; X: X, COMDAT
; _ This $ = ecx
; 7: X (){
Push ebp
Mov ebp, esp
Push ecx
Mov dword ptr _ this $ [ebp], ecx; ecx contains the starting address of member object x
; 8: I = 0;
Mov eax, dword ptr _ this $ [ebp]; The starting address of member object x to the eax register
Mov dword ptr [eax], 0; initialize the variable I in member object x
; 9: j = 0;
Mov ecx, dword ptr _ this $ [ebp]; The starting address of member object x to the ecx register
Mov dword ptr [ecx + 4], 0; the reason for initializing the number of member variables j plus 4 in member object x is that the address of j deviates from the starting address 4 byte of member object x (that is, the number of bytes of member variable I of member object x)
; 10 :}
Mov eax, dword ptr _ this $ [ebp]
Mov esp, ebp
Pop ebp
Ret 0
However, if the member object x does not have any constructor, what will happen?
The following is the c ++ source code:
Copy codeThe Code is as follows: class X {
Private:
Int I;
Int j;
};
Class Y {
Private:
Int I;
X x; // x member object
};
Int main (){
Y y;
}
The assembly code of the main function is as follows:
Copy codeThe Code is as follows:; 17: int main (){
Push ebp
Mov ebp, esp
Sub esp, 12; reserve 12 bytes of space for the object
; 18: Y y;
; 19 :}
Xor eax, eax
Mov esp, ebp
Pop ebp
Ret 0
As you can see, there is no function call in the main function, that is, the compiler does not provide the default constructor.
What if the member object x has a constructor with parameters (that is, a non-default constructor), and the object y does not have any constructor? The compiler reports an error.
This situation is similar to the previous one.
Based on the above situation, we can conclude that for a class without any constructor, the compiler will provide the default constructor. There are three situations:
1Class function virtual member function or inherit from virtual base class
2The base class of the class has constructor, and the base class constructor still displays the defined default constructor (not provided by the compiler ), if the base class constructor has parameters (that is, non-default constructor), the compiler reports an error.
3This situation is similar to the previous one. The class member objects have constructors, And the constructor of the member objects still displays the defined default constructor (not provided by the compiler ); if the constructor of a member object has parameters (non-default constructor), the compiler reports an error.
The above references the knowledge points in "in-depth explanation of VC ++" and their own analysis. please correct me.