The constructor of the C ++ global class and static class is called before the main function. However, in what order do constructors of different classes call them?
For the g ++ compiler, this sequence is determined by the file sequence when linking.
Let's use an example to illustrate this.
We have three files: t1.h, t1.cpp, and tt1.cpp. The content is
T. h
[Cpp]
# Ifndef T_H
# Define T_H
# Include <stdio. h>
Class {
Public:
A ();
};
Class B {
Public:
B () {a _ = NULL ;}
Void setA (A * a) {a _ = ;}
A * a () {return _;}
Static B _ B;
Private:
A * _;
};
Extern A * g_a;
# Endif
# Ifndef T_H
# Define T_H
# Include <stdio. h>
Class {
Public:
A ();
};
Class B {
Public:
B () {a _ = NULL ;}
Void setA (A * a) {a _ = ;}
A * a () {return _;}
Static B _ B;
Private:
A * _;
};
Extern A * g_a;
# Endif
Tt. cpp [cpp]
# Include "t. h"
B: _ B;
A * g_a = NULL;
A: ()
{
B: _ B. setA (this );
G_a = this;
}
# Include "t. h"
B: _ B;
A * g_a = NULL;
A: ()
{
B: _ B. setA (this );
G_a = this;
}
T. cpp
[Cpp]
# Include "t. h"
A;
Int main ()
{
Printf ("a = % p, B. a = % p, g_a = % p \ n", & a, B: _ B. a (), g_a );
}
# Include "t. h"
A;
Int main ()
{
Printf ("a = % p, B. a = % p, g_a = % p \ n", & a, B: _ B. a (), g_a );
}
T. h defines classes A and B. in the construction of A, A pays its own pointer to B: _ B. a _ and g_a.
Then, if you compile in this Order
[Plain]
G ++-o t tt. cpp t. cpp
G ++-o t tt. cpp t. cpp run./t. The result is:
[Plain]
A = 0x804a024, B. a = 0x804a024, g_a = 0x804a024
A = 0x804a024, B. a = 0x804a024, g_a = 0x804a024, which is the expected result.
If, compile in this Order
[Plain]
G ++-o t. cpp tt. cpp
G ++-o t. cpp tt. cpp:
[Plain]
A = 0x804a01c, B. a = (nil), g_a = 0x804a01c
A = 0x804a01c, B. a = (nil), g_a = 0x804a01c
So, why do we compile t. cpp first and compile tt. cpp to get the result of B. a to be null?
This should be related to the ELF file format.
In C/C ++, global variables and static variables are stored in the global data segment. When the elf file is loaded into the system, the data in the global data segment is directly mapped to the memory.
However, for C ++, constructor must be called for global and static class objects. The calling of these constructor objects is placed in the init segment. This section is a code segment that is executed when elf is loaded.
Naturally, g ++ places the structure of global class objects in the init segment in order of links.
Therefore, because t. cpp is first linked and tt. cpp is later linked, the constructor of a is called before the constructor of B: B. In this way, when A: A () is called, the value of B: B _: a _ is set as the pointer of.
When B: B () is called, the value of a _ is initialized to NULL.
Therefore, the final output result is B. a = (nil ).
This shows that in C ++, the results of accessing other global or static variables in the global constructor are unpredictable.
To solve this problem, we use pointer variables. For example, g_a.
The pointer variable is a variable rather than a Class Object. Therefore, when the elf file is mapped to the memory, the variable value of g_a is determined and no additional code is required. Therefore, this ensures that the correct value can be obtained by accessing the g_a variable at any time.