I have a Beta version of the graduation papers, please refer to you!
Chapter 1 Prepare Knowledge
C is an object-oriented high-level language. To understand some of its internal mechanisms, we must first familiarize with its binary code compilation process, and to understand the changes in the various regions in memory when running these binary code.
1. Procedures for the use of memory
Code area
Global data area
Pile area
Stack area
Code Area
Data Area
Stack Area
HEAP area
Any programs that need the CPU must be stored in memory in the form of binary code, so the storage methods and structures and structures in the program code are we first need to know. After the program gets the memory area assigned to its operating system, it divides this area into four parts, see the right:
Picture 1-1
These four areas store binary information with different logic meaning, please see the following code:
/
// Source Code 1.1
#include
Static int A = 5;
Static Int B = 1;
Class test
{
PUBLIC:
Test () {cout << "Create!" << endl;}
~ Test () {cout << "destroy!" << endl;
Private:
INT country;
}
Int foo (int x, int y)
{
Return X Y;
}
Void main (void)
{
INT i = 0;
i = foo (a, b);
COUT << "i =" << i << endl;
Test * ptest;
PTest = new test;
}
// Static global variable, stored in Data Area
// Static global variable, stored in Data Area
// Test, used to test the use of the HEAP area
//Constructor
// Destructor
// Private data
// Function, after the compiler generates code, store it in Code
In // am, the intermediate variable at the function is running, such as the ginseng X, Y
// and temporary variables generated by x y are stored in Stack
// AREA, when the function returns, the system automatically empties the function
// Press the variables in Stack Area
// main () function is the entry of the program, and the program in // CPU is set to Code Area when the program starts running.
// main () function location
// Declare pointer to the Test class
// new operator constructs an example of Test class in HEAP
/ / And point the pointer PTest point to the entrance address of the Test instance
Compile the above program, the system stores the code of each function to the code area, saves static variables, constants, and global variables in the global data area. When running, the CPU first executes the code in the main () function. The system is pressed into the local variable i of the main () function in the stack area. When the program runs to the call to the FOO () function, the system puts the CPU instruction register stack. And set its content to the address of the foo (), and control to the FOO () function. The FOO () function creates its local variable x, y in the stack area, and then returns the result in a manner of temporary variable to the caller after the operation of the X Y. After return, the system empties the variables stored in the stack area and puts the previous stack command register content out of the codes in main ().
After declaring Test * PTest, Main Creates a pointer variable in the stack area, then call New to create a TEST instance in the pile area and point PTest to it. Run the program, we can find that the constructor of the Test class is executed, but the destructor does not execute. The reason is that the variable value in the stack area is valid within the function of creating this variable, for example, the function foo returns, the shape X, Y is cleared from the stack. After the main returns, the pointer variable PTEST is also in the stack area. Clear; but the TEST instance in the pile of pointer points to the point, still exists, and the destructor of the class is not called. From this we can see the stack area and the stack area difference: Stack Area: The local data of the program, that is, local variables in each function.
Heap Area: Storage Program Run Dynamic Data
The use of the stack area is determined when compiling, and the use of the pile area is dynamically done in the program operation. The creation and deletion of the data in the pile area is to be controlled by the programmer. The system does not automatically clear it like a stack area, using the pile area to prevent the occurrence of the error in the example program, create an object with New, Be sure to delete it after use, or Memory Leak.
The difference between the stack area and the pile area, there will be further analysis.
The above is a brief introduction to the usage of each memory area during the execution of the program. The actual situation may be more complex than those described herein, such as the temporary variable of the transfer function returns value, how the function is cleared (involving calling specification __cdecl, __fastcall, and __stdcall). This section is mainly to understand the usage of the program, and do not analyze such details.
2. C Class memory pattern
C introduces the concept of Class, Class encapsulates a set of related data and the operation of these data, which is the basis for object-oriented programming. In the following section, I will do some analysis for the Class's memory layout.
We know that there is a member function and a member variable in the class, where member functions are divided into ordinary member functions, static member functions and virtual functions, and member variables are divided into ordinary member variables and static member variables. We started from the simplest situation. Please see the following code:
/
// Source Code 1.2
#include
Class test1
{
PUBLIC:
INT foo () {return 0;}
Private:
INT MEMBER_1;
FLOAT MEMBER_2;
Double * p;
}
Class test2
{
PUBLIC:
INT foo () {return 0;}
Virtual int v_foo () {return 0;}
Private:
INT MEMBER_1;
FLOAT MEMBER_2;
Double * p;
}
Class test3
{
PUBLIC:
INT foo () {return 0;}
Int static s_member;
Private:
INT MEMBER_1;
FLOAT MEMBER_2;
Double * p;
}
Class test4
{
PUBLIC:
INT foo () {return 0;}
Static int s_foo () {return 0;}
Private:
INT MEMBER_1;
FLOAT MEMBER_2;
Double * p;
}
Void main (void)
{
INT A = SizeOf Test1;
INT B = SIZEOF TEST2; INT C = SizeOf Test3;
INT D = SIZEOF TEST4;
COUT << "Size of class test1 is:" << a << endl
<< "Size of class test2 is:" << b << Endl
<< "Size of class test3 is:" << c << Endl
<< "SIZE OF Class Test4 IS:" << D << ENDL;
}
// TEST1 class, which encapsulates three ordinary member variables // a common member function
/ / Returns the ordinary member function of the INT value / / In the 32-bit system, the dimensions of each variable are as follows // sizeof (int) -> 4 // sizeof (float) -> 4 // sizeof (double *) -> 4
// TEST2 class adds virtual functions on the basis of Test1
// virtual function
// TEST3 class adds static member // variables on the basis of Test1
// Static member variable
// Class 4 class adds static to // engine function on the basis of TEST1
/ / Return to the static member function of the INT value
// Test the size of each class in Main
// Output: Size of class test1 IS: 12 // Output: Size of class test2 is: 16 // Output: size of class test3 is: 12 // Output: Size of class test4 is: 12
Now let's analyze the results of the program output:
The size of the TEST1 class is 12, just in the three ordinary member variable size, where is the member function foo () where? Leave this question and continue to look down. Test2 is more virtual than Test1, with the result size than 4 bytes, how can a function only have 4 bytes? Leave a second question and continue. TEST3 and TEST4 classes have more static member variables and static member functions than TEST1 classes, but this increase is not reflected in the size of the class, not solving!
By summarizing, we can sum up such facts
a. Include ordinary member variables, but does not include ordinary member functions
b. The virtual function is included in some form, but the function body itself is definitely not in the class.
c. Static member variables and static member functions are not included in the class
Think further, we can think of:
In order to improve performance, reduce the needs of memory, we don't have to include the functionality of the class in the entity of the class, create 10 TEST1 instances, which contains 10 different foo () codes that are very stupid . The class contains the class itself data (ordinary member variable), when the data is to be operated, the member function can be called by a certain manner. The class is actually calling Test1 :: foo () to access member functions (some of these details will be further elaborated in the "Package" chapter).
After seeing the problem of virtual functions, after increasing the virtual function, the size of the class has increased by 4 bytes, which happens to be a pointer size. We have reason to think that these 4 bytes are function pointers to virtual functions, so that we The second question can also be explained well. But why don't we use Test2 :: v_foo () to access the virtual function of the class? This issue will give a perfect answer in the "Polymorphic" chapter. (In fact, this pointer in the class points to the actual entrance address of the virtual function table rather than the virtual function, please refer to the "polymorphism" chapter)
Now let's take a look at the static member function and static member variables. According to the above ideas, everything is already very clear. Static members are shared for all instances of all classes, and there is no need to put them in the instance, which can operate them like the address member function: Test3 :: S_MEMBER and TEST4 :: S_FOO ().
By this map, you can understand the memory pattern of C Class.
Test :: v_foo ()
INT MEMBER_1
FLOAT MEMBER_2
Double * P
Pointer to the virtual function
TEST class
In fact, here is a virtual function table
Test :: foo ()
Test :: s_foo ()
Test :: s_member
Ordinary member function
Static member function
Static member variable
Figure 1-2
3. Differences between compile periods and running periods
The compile period is the process of transformation of the source code to the binary instruction, while the runtime is the process of executing these instructions in the CPU.