C ++ virtual function Polymorphism

Source: Internet
Author: User

1. What are virtual functions and polymorphism?

 

A virtual function is a member function declared as virtual in a class. When the compiler sees that such a function is called through a pointer or reference, it is bound to it later, that is, through a pointer (or reference) the type information of the class to which the function belongs. Generally, such pointers or references are declared as base classes. They can refer to objects of base classes or derived classes.
Polymorphism means that the same method can have different behaviors based on different objects (according to your understanding, I do not know whether it is rigorous to say so ).

The following is an example of virtual functions, polymorphism, early binding, and late binding:
Two siblings (Brother and Sister) of Lee attended the sports meeting with different surnames (team members with different surnames), the men's project competition, and the women's project competition. At the opening ceremony, representatives of participating teams made speeches, both of them wanted to show their faces, but they could only go with one person. They finally decided to capture the decision, and the organizing committee did not disagree, so they didn't care whether their brother or sister came to speak, you only need to send a sentence with the surname Li. The sports meeting was held as scheduled. My sister captured the opportunity to speak on behalf of the Li family. My brother participated in the men's project competition and my sister participated in the Women's Project Competition. We are not concerned about the results of the competition.
Now let's make an analogy (only discuss topics related to sports meetings ):
(1) Class Design:
Li's siblings belong to the Li family. Li's is a base class (here it is still an abstract pure base class), and Li's derived two sub-classes (Li's male and Li's female ), lee's male Association all men's project competitions (Lee's male member function), Lee's female Association all women's project competitions (Lee's female member function ). All people surnamed Li will speak (basic functions). Of course, Li's male and Li's female will also speak from Li's, but the voices and content of men and women are different, it makes people feel different (Li's male and Li's female respectively redefine the virtual function ). The two siblings are the entities of the male and female.
(2) Program Design:
Enter the registration form for the contestant.
(3) Compile:
The registration form of brother and sister li was handed over to the Organizing Committee (compiler). His brother and sister participated in the men's and women's competitions respectively. The Organizing Committee understood at a Glance (early binding ), however, the Spokesman's selection is not clear. The Organizing Committee saw the "Li Jia representative" (base class pointer) on the registration form. The Organizing Committee did not determine who it was, so it made a remark: If it was a male, it is her brother Li moumou; if it is a female, it is her sister li moumou (late binding ). After completing other preparations, the Organizing Committee will wait for the sports meeting to begin (compilation is complete ).
(4) program running:
The sports meeting started (the program started to run). At the opening ceremony, we heard a speech from sister li. If my brother is lucky enough to win, we will hear a speech from his brother (polymorphism ). Then we can see the two sisters in the competition...

I hope this metaphor makes it clear the concept of virtual functions, polymorphism, early binding, and late binding and their relationships. In addition, early binding means that the compiler knows the specific type of the object during compilation and determines the exact address of the member function called by the object; late binding obtains the virtual function table pointer of the Class Based on the type information of the object indicated by the pointer, and then determines the exact address for calling the member function.

2. Uncover late-bound secrets

What is the late binding of virtual functions implemented by the compiler? Let's find out.

The compiler creates a table (called V ta B L E) for each class containing virtual functions ). In V ta B L E, the compiler places the virtual function address of a specific class. In each class with a virtual function, the compiler secretly sets a pointer called V p o I n t e r (abbreviated as V p t r ), V ta B l e pointing to this object. When using a base class pointer for a virtual function call (that is, for a multi-state call), the compiler statically inserts the V p t r, find the code of the function address in the V ta B l e table, so that the correct function can be called to enable later binding. Set v ta B L E for each class, initialize v P T R, and insert code for the virtual function call. All of this happens automatically, so we don't have to worry about this. Using Virtual functions, the appropriate functions of this object can be called, even if the compiler does not know the specific type of the object. (C ++ programming ideas)

No displayed type information exists in any class, and class information must be stored in the object. Otherwise, the type cannot be created at runtime. What is the class information? Let's look at the following classes:

Class no_virtual
{
Public:
Void fun1 () const {}
Int fun2 () const {return ;}
PRIVATE:
Int;
}

Class one_virtual
{
Public:
Virtual void fun1 () const {}
Int fun2 () const {return ;}
PRIVATE:
Int;
}

Class two_virtual
{
Public:
Virtual void fun1 () const {}
Virtual int fun2 () const {return ;}
PRIVATE:
Int;
}

Among the above three categories:
No_virtual has no virtual function. sizeof (no_virtual) = 4. The length of class no_virtual is the length of its member variable integer;
One_virtual has a virtual function, sizeof (one_virtual) = 8;
Two_virtual has two virtual functions, sizeof (two_virtual) = 8; there is no difference in the length of the class between a virtual function and two virtual functions, in fact, their length is the length of no_virtual plus the length of a void pointer, it reflects, if one or more virtual functions, the compiler inserts a pointer (V p t r) in this structure ). There is no difference between one_virtual and two_virtual. This is because v p t r points to a table with a storage address and only needs a pointer, because all the virtual function addresses are included in this table.

This vptr can be seen as the type information of the class.

Let's see how the compiler creates the virtual function table pointed to by vptr. Let's take a look at the following two classes:
Class base
{
Public:
Void bfun (){}
Virtual void vfun1 (){}
Virtual int vfun2 (){}
PRIVATE:
Int;
}

Class derived: public Base
{
Public:
Void dfun (){}
Virtual void vfun1 (){}
Virtual int vfun3 (){}
PRIVATE:
Int B;
}

The virtual function tables (vtables) pointed to by two vptr classes are as follows:
Base Class
------
Vptr --> | & base: vfun1 |
------
| & Base: vfun2 |
------

Derived class
-------
Vptr --> | & derived: vfun1 |
-------
| & Base: vfun2 |
-------
| & Derived: vfun3 |
-------

Whenever you create a class that contains a virtual function or derive a class from a class that contains a virtual function, the compiler creates a vtable for this class, as shown in. In this table, the compiler places the addresses of all functions declared as virtual in this class or its base class. If the function declared as virtual in the base class is not redefined in this derived class, the compiler uses the virtual function address of the base class. (In the derived vtable, This is the entry of vfun2 .) Then the compiler places vptr in this class. When simple inheritance is used, there is only one vptr for each object. Vptr must be initialized to point to the corresponding vtable, which occurs in the constructor.
Once the vptr is initialized to point to the corresponding vtable, the object "knows" what type it is. However, this kind of self-cognition is useful only when virtual functions are called.

Vptr is often at the beginning of an object. The compiler can easily obtain the value of vptr to determine the location of the vtable. Vptr always points to the starting address of vtable. The virtual function addresses of all base classes and their subclasses (except virtual functions defined by sub-classes) are always stored in the same vtable, for example, In the vtable of the base and derived classes above, the vfun1 and vfun2 addresses are always stored in the same order. The compiler knows that vfun1 is at vptr and vfun2 is at vptr + 1. Therefore, when using a base class pointer to call a virtual function, the compiler first obtains the type information (vptr) of the pointer to the object ), then call the virtual function. For example, if a base class pointer pbase points to a derived object, then pbase-> vfun2 () is called by the compiler and translated as vptr + 1, because the address of the virtual function vfun2 is located at the Index 1 in the vtable. Similarly, pbase-> vfun3 () is called by the compiler and translated as vptr + 2. This is the so-called late binding.

Let's take a look at the assembly code of virtual function calls to deepen our understanding.

Void test (base * pbase)
{
Pbase-> vfun2 ();
}

Int main (INT argc, char * argv [])
{
Derived TD;

 
Test (& TD );

Return 0;
}

The compilation code generated by derived TD; is as follows:
MoV dword ptr _ TD $ [esp + 24], offset flat :?? _ 7derived @ 6B @; derived: 'vftable'
According to the comments of the compiler, the vtable address of the derived class is stored in PTR _ TD $ [esp + 24.

The compilation code generated by test (& TD); is as follows:
Lea eax, dword ptr _ TD $ [esp + 24]
MoV dword ptr __$ ehrec $ [esp + 32], 0
Push eax
Call? Test @ yaxpavbase @ Z; Test
When the test function is called, the following work is completed: Get the address of the object TD, press it on the stack, and then call test.

Pbase-> vfun2 (); The compilation code is as follows:
MoV ECx, dword ptr _ pbase $ [esp-4]
MoV eax, dword ptr [ECx]
Jmp dword ptr [eax + 4]
First, the address of the object directed by the pbase pointer is taken from the stack and assigned to ECx. Then, the address in the pointer variable starting with the object is assigned to eax. The value of eax is the value of vptr, that is, the vtable address. Finally, the virtual function is called. Because vfun2 is located at the second position of vtable, it is equivalent to vptr + 1, and each function pointer is 4 bytes long, therefore, the final call is translated into jmp dword ptr [eax + 4] by the compiler. If you call pbase-> vfun1 (), this statement should be compiled as jmp dword ptr [eax].

Now you should have a clear understanding of polymorphism, virtual functions, and late binding.
If you have any mistakes, please discuss them!

Related Article

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.