C ++ virtual functions and polymorphism learning notes

Source: Internet
Author: User

 

C ++ virtual functions and polymorphism learning notes:

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 pointer pbase points to a derived object
Pbase-> vfun2 () is called by the compiler into vptr + 1 because the vfun2 address of the virtual function 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].

 

Virtual function: Just to point to different inherited class functions through the base class pointer, for the convenience of extended functions and so on?

 

Pure virtual function: The base class is an abstract class to prevent the creation of objects ??

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.