The c ++ compiler summarizes the implementation principles of polymorphism and the compiler polymorphism.

Source: Internet
Author: User

The c ++ compiler summarizes the implementation principles of polymorphism and the compiler polymorphism.

Question: Define an empty type without any member variables or member functions. What is the result of sizeof operation on this type?

The result is 1. Because an empty instance does not contain any information, the result is 0 after sizeof calculation, but when declaring an instance of any type, the memory must occupy a certain amount of space. Otherwise, these instances cannot be used. The Compiler determines the memory size.

Continue: if you add a constructor and destructor to this type, what is the result?

Or 1, because we only need to know the address of the function to call constructor and destructor, and the address of these functions is only related to the type and has nothing to do with the type instance, the compiler does not add any additional information to the two functions in the instance.

Continue: What if I change the destructor to a virtual function? What is the result?

The c ++ compiler will generate a virtual function table for this type if it finds a virtual function in the type, add a pointer to the virtual function table to each instance of this type. on 32-bit machines, the pointer type is 4 bytes and the result is 4 to 64-bit machines, the pointer size is 8 bytes and the result is 8.

Implementation of object-oriented Polymorphism

Polymorphism: The same invocation statement has many different manifestations.

See the following code example:

class animal{public:    void sleep()    {        cout<<"animal sleep"<<endl;    }    void breathe()    {        cout<<"animal breathe"<<endl;    }};class fish:public animal{public:    void breathe()    {        cout<<"fish bubble"<<endl;    }};int main(void){    fish fh;    animal *pAn=&fh;    pAn->breathe();    return 0;}    

The parent class Pointer Points to the subclass object and calls the breathe method. The result is animal breathe, that is, the breathe method of the parent class is called. This does not achieve polymorphism. The C ++ compiler determines the address of the function called by each object during compilation, which is called the early binding ), when the fh address of the fish object is assigned to the pAn pointer of the parent class, the C ++ compiler performs type conversion, it holds that the pAn pointer variable of the parent class stores the address of the animal object. When pAn-> breathe () is executed in the main () function, the breathe function of the animal object is called.

Further speaking:

When constructing an object of the fish class, we first need to call the constructor of the parent class: animal class to construct the object of the animal class, then, we can call the fish constructor to construct a part of the object and splice a complete fish object. When a fish object is converted to the animal type, the object is considered to be the upper part of the original object's entire memory model, that is, the "memory occupied by animal objects" in the figure ".

When the method is called using the object pointer after the type conversion, that is, the method in the memory where it is located is also called. Therefore, the output is animal breathe. This is not a manifestation of polymorphism.

Three conditions for Polymorphism implementation

The premise is that there must be an inheritance relationship, and then we need the parent class pointer (reference) to call the subclass object. The key is that the subclass has a rewrite of the virtual function of the parent class. The virtual keyword tells the compiler that this function must support polymorphism. Instead of judging how to call a method based on the pointer type, we should judge how to call the function based on the actual object type pointed to by the pointer.

Theoretical Basis of Polymorphism

In the preceding example, the output result is that the compiler determines the address of the function called by the object during compilation. To solve this problem, we need to use the late binding technology. When the compiler uses late binding, it will determine the object type and call the function correctly at runtime. To enable the compiler to bind a function later, you must use the virtual keyword when declaring the function in the base class. Such a function is called a virtual function. Once a function is declared as virtual in the base class, the function is virtual in all the derived classes, and it does not need to be explicitly declared as virtual.

The so-called Dynamic Association: determines the call of the rewrite function based on the actual object type.

Implementation principle of polymorphism in C ++

When a virtual function is declared in a class, the compiler will generate a virtual function table in the class. The virtual function table is a data structure that stores the function pointers of class members, the virtual function table is automatically generated and maintained by the compiler. virtual member functions are put into the virtual function table by the compiler. When virtual functions exist, each object has a pointer to the virtual function table (vptr pointer)

 

The compiler provides a virtual table pointer vptr for each class object. This Pointer Points to the virtual function table of the class to which the object belongs. When the program is running, initialize vptr Based on the object type, so that vptr points to the virtual table of the class correctly, so that the correct function can be found when the virtual function is called.



In the above example:

    fish fh;    animal *pAn=&fh;    pAn->breathe();

Because the parent class pointer pAn actually points to the object type of the subclass, vptr points to the subclass fish class vtable. When pAn-> breathe () is called, the breathe () function of the fish class is found based on the function address in the virtual table. It is precisely because the virtual functions called by each object are indexed by the virtual table pointer that determines the correct initialization of the virtual table pointer. In other words, we cannot call the virtual function before the virtual table pointer is correctly initialized.

When or where is the virtual table pointer initialized?
C ++ creates virtual tables and initializes virtual table pointers in constructors.

Call Sequence of constructor: when constructing a subclass object, you must first call the constructor of the parent class. At this time, the compiler only "sees" the parent class and does not know whether there are successors after it, it initializes the virtual table pointer vptr of the parent class object, which points to the virtual table of the parent class. When a subclass constructor is executed, the virtual table pointer vptr of the subclass object is initialized, and vptr points to its own virtual table. After the fh object of the fish class is constructed, its internal virtual table pointer is initialized to a virtual table pointing to the fish class.

After type conversion, call pAn-> breathe (). Because pAn actually points to a fish object, the virtual table pointer inside the object points to a virtual table of the fish class, therefore, the breathe () function of the fish class is called.
 

Note:

The virtual function table pointer VPTR is used to call the rewrite function when the program is running. Therefore, the addressing operation is required to determine the function to be called. While normal member functions determine the called functions at compilation. In terms of efficiency, the efficiency of virtual functions is much lower. For efficiency, it is not necessary to declare all member functions as virtual functions.

When an object is created, the VPTR pointer is initialized by the compiler. The VPTR pointing is final only after the object construction is complete, whether the VPTR of the parent class object points to the parent class virtual function table or the VPTR of the subclass object points to the subclass virtual function table.

Back to the start Question:

Class A {void g (){.....}}; sizeof (A) = 1; if it is changed to the following: class A {public: virtual void f (){......} void g (){.....}}

Sizeof (A) = 4, because there is A virtual function in Class A, in order to achieve polymorphism, each class containing virtual functions implicitly contains a Static virtual pointer vptr pointing to the static virtual table vtable of the class, and the table items in vtable point to the entry address of each virtual function in the class.

Polymorphism is implemented by dynamic binding of the program, rather than static binding of the call method of the object during compilation.

When running the program to dynamic binding, find the object type pointed to by the base class pointer through vptr and call the corresponding method to realize polymorphism. This is the dynamic binding or lazy compile ).

class base;base *pbase;class base{public:    base()    {        pbase=this;    }    virtual void fn()    {        cout<<"base"<<endl;    }};class derived:public base{    void fn()    {        cout<<"derived"<<endl;    }};derived aa;int main(void){    pbase->fn();    return 0;}

In the base class constructor, save this pointer to the pbase global variable. When defining the Global Object aa, that is, calling derived aa;, you need to call the constructor of the base class, first construct the part of the base class, and then the part of the subclass, the two parts are spliced to produce the complete object aa.

This Pointer Points to the aa object of course, so we use pbase to call fn () in the main () function, because pbase actually points to the aa object, the virtual table pointer inside the aa object points to its own virtual table, and the final call is of course the fn () function in the derived class.

When declaring the fn () function in the derived class, I forgot to add the public keyword, resulting in the Declaration as private (the default value is private). However, through the above-mentioned virtual function call mechanism, this does not affect the output of correct results. I don't know if this is a Bug in C ++, because the virtual function is called at runtime to determine which function to call, so when the compiler is compiling, it is unknown that pbase points to an aa object, which causes this strange phenomenon. If aa objects are called directly, the compiler usually uses early binding because the object type is determined (note that aa is an object variable, not a pointer variable, determine the called function during compilation. Therefore, fn () is private and cannot be called directly.

What if I call a virtual function directly in the base class constructor?

When calling the base class constructor, the compiler only "sees" the parent class and does not know whether there is a successor behind it. It only initializes the virtual table pointer of the parent class object, make the virtual table pointer point to the virtual table of the parent class, so the result is incorrect. C ++ polymorphism can be applied only after the sub-class constructor is called and the entire virtual table is constructed. In other words, do not call virtual functions in constructor to realize polymorphism. Of course, it doesn't matter if you just want to call the functions of this class.

Conclusion:

The constructor calls a multi-state function and cannot implement polymorphism.

Comparison between virtual functions and pure virtual functions

Virtual Functions

Reason for introduction: to facilitate the use of polymorphism, we often need to define virtual functions in the base class.

Pure virtual functions

Reason for introduction: In order to realize polymorphism, pure virtual functions are a bit like interfaces in java. Instead of implementing the process, let them inherit from its subclass. In many cases, it is unreasonable for the base class to generate objects. For example, an animal can be derived from sub-classes such as tigers and peacocks as a base class, but the object generated by the animal itself is obviously unreasonable. In this case, the animal class is defined as an abstract class, that is, a class that contains pure virtual functions. Pure virtual functions are the base class that only defines the function body and there is no implementation process:

Virtual void Eat () = 0; directly = 0. do not define it in cpp.

Differences between virtual functions and pure virtual functions
A function in a virtual function is implemented, even if it is a void implementation. It is used to implement a dynamic binding of a function that can be overloaded in a subclass, while a pure virtual function is an interface, it is a function declaration and is not implemented in the base class. It must be implemented in the subclass.
Virtual functions can not be overloaded in subclasses, but must be implemented in subclasses.

Summary:

For a virtual function call, each object has a virtual table pointer, Which is initialized as a virtual table of this class. Therefore, no matter how your object type is converted in the program, the virtual table pointer inside the object is fixed, so dynamic object function calls can be realized, this is how C ++ polymorphism is implemented.

If the base class has virtual functions:

1. Each class has a virtual table.

2. The virtual table can inherit. If the subclass does not override the virtual function, the virtual table of the subclass still has the address of this function, but this address points to the implementation of the virtual function of the base class. If the base class has three virtual functions, the base class's virtual table has three (virtual function address), and the derived class also has a virtual table, at least three, if the corresponding virtual function is rewritten, the address in the virtual table changes and points to its own virtual function implementation. If the derived class has its own virtual function, this item is added to the virtual table.

3. The virtual function addresses in the virtual table of the derived class are arranged in the same order as those in the virtual table of the basic class.

 

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.