Diverse and interactive winform UI design and development ideas (flash, HTML, etc)

Source: Internet
Author: User

Guidance:
  
How can virtual functions be called to implement virtual functions "? As a manifestation of C ++ polymorphism, it is estimated that many people are interested in its implementation mechanism. When talking about the powerful mechanism of C ++ in general textbooks, we will teach you how to use and when to use it, instead of exploring the real implementation details of this virtual function. (Of course, different compiler manufacturers may have their own implementations of virtual functions. Haha, this is even the "polymorphism" of virtual functions for the compiler :). As a compilation language, the final result of C ++ compilation is a bunch of Assembly commands (which is different from. Net CLR ). Today, I will unveil the secrets of virtual functions and look at the implementation of virtual functions in terms of assembly. Let everyone know more about the implementation of virtual functions.(The program environment in this article is:PC + Windows XP Pro + visual c ++ 6.0, The results obtained in this article and the reflected compiler policy are onlyVc6.0Compiler)
  
Let's take a look at a simple piece of code:
  Code segment:
Line01: # include
Line02:
Line03: class base {
Line04: public:
Line05: void _ stdcall output (){
Line06: printf ("class base/N ");
Line07 :}
Line08 :};
Line09:
Line10: Class derive: public base {
Line11: public:
Line12: void _ stdcall output (){
Line13: printf ("class derive/N ");
Line14 :}
Line15 :};
Line16:
Line17: void test (base * P ){
Line18: p-> output ();
Line19 :}
Line20:
Line21: int _ cdecl main (INT argc, char * argv []) {
Line22: derive OBJ;
Line23: Test (& OBJ );
Line24: Return 0;
Line25 :}
  
The program running result will be:
Class base
  
Change the output function declaration (line05) of the base class:
Virtual void _ stdcall output (){
Obviously, the program running result will be:
Class derive
  
The test function recognizes this pointer as a pointer to a derive Class Object and correctly calls its output function. How does the compiler achieve this? Let's take a look at the differences between the "virtual" keyword and the "virtual" keyword in the final assembly code.
  
(Before explaining the assembly code below, let's perform a simple scan on the Assembly. Of course, if you are familiar with assembly, go to the brackets and leave them blank. Let's talk about the output function declared _ StdcallCall method: It indicates that when a function is called, the parameter is pressed from right to left. After the function is called, the caller restores the stack pointer ESP. Other Call methods are described in this document. This pointer of C ++: the initial address of an object. During function execution, its parameters and variables in the function will have the following stack structure:
  
  
(Figure 1)
As shown in 1, both our parameters and local variables are expressed in EBP addition or subtraction. You may have a question: Sometimes my parameters or local variables may be a large struct or only a char. Why is the EBP plus or minus a multiple of 4 here? Well, for 32-bit machines, four bytes are used, that is, 32 bits are transmitted each time, to achieve the best bus efficiency. If your parameter or local variable is larger than four bytes, it is split into four bytes each time. If it is smaller than four bytes, it is still four bytes each time. Let's briefly explain the Assembly commands used in explain. These commands are well known:
① Mov destination, Source
Assign the value of source to destination. Note: "[XXX]" is often used below. "XXX" corresponds to a register plus or minus a certain number, "[XXX]" indicates the content of the memory unit corresponding to the value of "XXX. For example, "XXX" is a key to open a drawer, and then extract the items in the drawer to others, or put the items in the drawer;
② Lea destination, [Source]
Assign the value of source to destination. Note that this command is to give the source to destination, instead of assigning the content of the memory unit corresponding to the source to destination. It is like giving the key to someone else;
If you want to view the disassembly during debugging, you should click the rightmost button under figure 2.
  
(Figure 2)
Other commands I guess you can know what it is from its name. If you want to know its specific meaning, please refer to the compilation manual. :)
  
1. When there is no virtual Keyword:
(1) disassembly of the main function:
Line22: derive OBJ;
Line23: Test (& OBJ );
// If you set the breakpoint to 22 rows, the VC will tell you that this is an invalid line when Debugging starts.
// Automatically move the vertex to the next line (line23) because the Code does not define the constructor for the derive and its base class.
// Number, and the compiler does not generate a default constructor for it. This line of C ++ code will not generate
// Any assembly instructions that can be actually called;
   004010d8 Lea Eax, [ebp-4]
// Put the object OBJ address into the eax register;
   004010db Push Eax
// Add parameters to the stack;
004010dc call @ ILT + 5 (test) (0040100a)
// Call the test function;
// Here @ ILT + 5Is the address of the JMP command to jump to the test function, all
// Function call will be like this @ ILT + 5 * n, NIndicates NFunctions, while ILTMeaning
// Yes Import lookup tableWhen a program calls a function, it uses this table to jump to the corresponding function.
// Line code.
004010e1 add ESP, 4
// Adjust the stack pointer. The test function was called just now. The call method is _ cdecl, which is used by the caller to restore the stack pointer;
(2) disassembly of the test function:
Line18: p-> output ();
00401048 mov eax, dword ptr [EBP + 8]
// Here [EBP + 8] is actually the leftmost parameter of the test function, which is the eax of the medium pressure stack of the main function above;
// Put the parameter value (that is, the address of the OBJ object in the main function above) into the eax register.
// Note: C ++ Class member function, the default call method is" _ Thiscall ", This is not a process
// The keyword specified by the sequencer, which indicates the function call. The parameter pressure stack is from right to left, and ECX Storage
// To save This Pointer. Here our Output The function call method is" _ Stdcall ", ECX Register
// Not used for saving This Pointer, so there must be additional instructions This Pointer pressure Stack:
   0040104b Push Eax
// Import eax into the stack, that is, the this pointer required to call the output function below;
0040104c call @ ILT + 0 (base: Output) (00401005)
// Call the member functions of the class without any suspense. honestly call the output functions of the base class;
  
2. When the virtual keyword exists:
(1) disassembly of the main function:
Line22: derive OBJ;
// When there is a virtual keyword, set the breakpoint to 22 rows, and the debugging will stop here. We do not
// Declare the constructor for the derive class or its base class. This indicates that the compiler automatically generates a constructor for the class.
// Number. Next we can see what the constructor automatically generates;
   00401088 Lea ECX, [ebp-4]
// Put the object OBJ address into the ECX register. Why? As mentioned above ~
00401_ B call @ ILT + 25 (derive: derive) (0040101e)
// The Compiler helps generate a constructor. What does it do here? Wait for a while. Make a mark first :// @_@1; put the OBJ address in ECx above to prepare for this function call;
Line23: Test (& OBJ );
// This call operation is the same as the preceding virtual Keyword:
   00401090 Lea Eax, [ebp-4]
   00401093 Push Eax
00401094 call @ ILT + 5 (test) (0040100a)
   004010c9 Add ESP, 4
(2) disassembly content of the test function (which is different from the above when there is no virtual keyword ):
Line18: p-> output ();
00401048 mov eax, dword ptr [EBP + 8]
// Put the value of the first parameter of test into the eax register. You should have known that this is the OBJ // address;
0040104b mov ECx, dword ptr [eax]
// Oh, get the content of the address corresponding to the number in the eax register. Do you know what this is? Before you start. // make a mark first: @_@2
0040104d mov ESI, ESP
// This is used for ESP pointer detection.
0040104f mov edX, dword ptr [EBP + 8]
// Store the OBJ address in the edx register again. You should know that it is actually the this pointer, And this is prepared for // calling the member functions of the class;
00401052 push edX
// Import the Object Pointer (that is, this pointer) into the stack to prepare for calling the member functions of the class;
00401053 call dword ptr [ECx]
// This call is a member function of the class. Do you know which function to call? Wait for a while. Make a mark first:
// @_@3
00401055 cmp esi, ESP
// Compare the ESP pointer. If they are different, the following _ chkesp function will let the program go to debug
00401057 call _ chkesp (00401110)
// Detect the ESP pointer and handle possible stack errors (if an error occurs, it will fall into Debug ).
  
For a C ++ class, if it is to present polymorphism (the General compiler will take this class and whether its base class contains the virtual keyword as the class to be polymorphism ), the class has a virtual table, and each instance (object) has a virtual pointer (vptr) pointing to the virtual function table of the class, as shown in 3:
  
(The vfuncaddr in the table on the right below should be understood as the address of the memory unit storing the virtual function address. More accurately, it should be the address of the JMP command to jump to the corresponding function .)
  
  
(Figure 3)
First, we will analyze the object OBJ of the derive class in our main function to see its memory layout. Since there is no data member, its size is 4 bytes and there is only one vptr, so the OBJ address is the vptr address. (The Class I used here does not have data members, because different compilers may place the vptr in different object memory la S. Of course, it is generally not placed in the object header, for example, the Microsoft compiler is placed at the end of the object. In either case, the "OBJ address, that is, the vptr address" in this example is true .)
The vptr of an object is not specified by the programmer, but is specified by the compiler during compilation. Now let me explain @_@1- @_@3.
  
   @_@1 :
That is, why does the compiler generate a default constructor for us? Let's look for the answer from the disassembly:
  
This is the core assembly segment selected from the derive constructor generated by the compiler by default:
004010d9 pop ECx
// By default, the calling method of the derive constructor generated by the compiler is _ thiscall, so the ECX register, as shown in the previous
// This pointer is saved, that is, the address of the OBJ object, which is also the vptr address;
// I found that even if you declare a constructor AS _ stdcall, it is also the same as the disassembly of the default _ thiscall.
// This is different from the member function;
004010da mov dword ptr [ebp-4], ECx
// For the member function of the class called by the _ thiscall method, the first local variable is always this pointer, And the ebp-4 is
// Address of the first local variable of the Function
004010dd mov ECx, dword ptr [ebp-4]
// Because the base class constructor needs to be called, The this pointer must be assigned to the ECX register;
004010e0 call @ ILT + 30 (base: Base) (00401023)
// Execute the constructors of the base class;
004010e5 mov eax, dword ptr [ebp-4]
// Put this pointer into the eax register;
004010e8 mov dword ptr [eax], offset derive: 'vftable' (0042201c)
// Put the first address of the virtual function table into the address pointed to by this pointer, that is, the vptr is initialized;
  
As you can see, the compiler generates a default constructor, which is used to initialize vptr. You can probably think about what the base constructor has done, it is also used for vptr initialization:
0040d769 pop ECx
0040d76a mov dword ptr [ebp-4], ECx
0040d76d mov eax, dword ptr [ebp-4]
0040d770 mov dword ptr [eax], offset base: 'vftable' (00422020)
You don't have to explain it. Just like the constructor function of derive, vptr is initialized. If you declare and define a constructor yourself, you will first execute the code for vptr initialization and then execute your code. (If you have a value assignment code in the form of the initialization list of the constructor, you will first execute the value assignment code in your initialization list, then, initialize the vptr of the class and execute the code in the constructor)
  
   @_@2 And @_@3 :
00401048 mov eax, dword ptr [EBP + 8]
0040104b mov ECx, dword ptr [eax]
Here, the First Command stores the OBJ address in eax, so you should know that the first four bytes of the memory unit corresponding to the OBJ address are actually vptr addresses? The content of the memory unit corresponding to the vptr address is actually the starting address of the vftable table, and the content of the memory unit corresponding to the vftable table address is the virtual function address. Use a more explicit representation (4. This figure shows the table corresponding to the content in the address and address unit. Note: The address in the vftable table on the right is not actually the real function address, but the address of the JMP command to jump to the function, for example, 0x0040ef12, not the real class :: the address of the XXX function, but the address of the JMP Instruction of the class: XXX function ). In this way, ECx stores the address of the memory unit of the derive: output function address, and then calls:
0040104f mov edX, dword ptr [EBP + 8]
00401052 push edX
00401053 call dword ptr [ECx]
Then jump to the corresponding function to execute the function.
(If there are multiple virtual functions that call the nth virtual function, the preceding Call Command will be changed to the following form: call dword ptr [ECx + 4 * (N-1)])
  
The above compilation is like this: I got a key, opened a drawer, and took out the stuff inside, but it was still a key, and I had to hold the key to open another drawer, take out the real things in it. Pai_^
  
  
(Figure 4)
Knowing the ins and outs, if someone else can call the corresponding virtual functions using assembler, what should I do if I want to use C/C ++? I think you should have an eye. Let's see how I did it. (the following uses a C function pointer to call a member function of the C ++ class and converts a member function of the C ++ class to a C function, you need to do this: C The number of parameters of a function C ++ One more member function of the class is used as the first parameter, and it must be the address of the Class Object.):
Declare the output function of the base class as virtual, and then change the main function:
Int _ cdecl main (INT argc, char * argv []) {
Derive OBJ; // The object must still have
Typedef void (_ stdcall * pfunc) (void *); // declare the function pointer
Void * pthis = & OBJ; // get the object address, which is used as the this pointer
// Figure 4 assigns 0x0012ff24 to pthis
Pfunc = (pfunc) * (unsigned int *) pthis; // obtain the content of this address, which corresponds to Figure 4.
// The content of the address 0x0012ff24 is
// 0x00400112
Pfunc = (pfunc) * (unsigned int *) pfunc; // obtain the content of this address, corresponding to Figure 4
// The content of the address 0x00400112 should be
// 0x0040ef12, that is, the function address
Pfunc (pthis); // The execution function will execute derive: Output
Return 0;
}
Run the command to check the result. I have not used an object or a pointer to a class to call a function. J
  
This time you should know what is going on with the virtual function? This section describes how to implement virtual functions based on the Microsoft Vc ++ 6.0 compiler. The methods and policies used by the compiler to implement C ++ can be explored from its disassembly statements. Understanding these underlying details will greatly improve your C/C ++ code! I hope this article will help you. Please mailto: tigger_211@sina.com for any questions or advice.
Trackback: http://tb.blog.csdn.net/TrackBack.aspx? Postid = 82606

This article is transferred from
Http://blog.csdn.net/apemancsdn/archive/2004/08/23/82606.aspx

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.