The Realization and Principle of C++ Polymorphism

Original link: http://www.cnblogs.com/cxq0017/p/6074247.html


The Realization and Principle of C++ Polymorphism

  The polymorphism of C++ can be summed up in one sentence: add the virtual keyword before the function of the base class, rewrite the function in the derived class, and the runtime will call the corresponding function according to the actual type of the object .

  If the object type is a derived class, the function of the derived class is called; if the object type is a base class, the function of the base class is called

  • A function declared with the virtual keyword is called a virtual function, and a virtual function must be a member function of a class.

  • A class with virtual functions has a one-dimensional virtual function table called a virtual table, and the object of the class has a virtual pointer to the beginning of the virtual table. The virtual table corresponds to the class, and the virtual table pointer corresponds to the object .

  • Polymorphism is a variety of implementations of an interface, the core of object-oriented, and is divided into class polymorphism and function polymorphism.

  • Polymorphism is implemented with virtual functions, combined with dynamic binding.

  • A pure virtual function is a virtual function plus = 0;

  • An abstract class is a class that includes at least one pure virtual function.

  • Pure virtual function: virtual void fun()=0; that is, an abstract class! This function must be implemented in the subclass, that is, the name first, no content, and the content is implemented in the derived class.

Let's look at an example

#include "stdafx.h"
#include <iostream> 
#include <stdlib.h>
using namespace std; 

class Father
{
public:
    void Face()
    {
        cout << "Father's face" << endl;
    }

    void Say()
    {
        cout << "Father say hello" << endl;
    }
};


class Son:public Father
{
public:     
    void Say()
    {
        cout << "Son say hello" << endl;
    }
};

void main()
{
    Son son;
    Father *pFather=&son; // 隐式类型转换
    pFather->Say();
}

The output is:

Father say hello

  In the main() function, we first define an object son of the Son class, and then define a
pointer variable pFather that points to the Father class, and then use this variable to call pFather->Say(). It is estimated that many people tend to associate this situation with The polymorphism of c++ is confusing, thinking that son is actually an object of the Son class, it should be the Say that calls the Son class, and outputs "Son say hello", but the result is not.

From a compilation point of view

  When the c++ compiler compiles, it must determine the address of the function (non-virtual function) called by each object, which is called early binding . When we assign the address of the Son class object son to pFather, the c++ compiler will perform After the type conversion, the C++ compiler thinks that the variable pFather holds the address of the Father object. When pFather->Say() is executed in the main function, of course, the Say function of the Father object is called.

 From the perspective of memory
 
 write picture description here
 
, when   we construct the object of the Son class, we first call the constructor of the Father class to construct the object of the Father class, and then call the constructor of the Son class to complete the construction of its own part, thereby splicing out a complete Son class object . When we convert the Son class object to the Father type, the object is considered to be the upper half of the entire memory model of the original object, that is, the "memory occupied by the Father object" in the above figure , then when we use the converted type When the object pointer calls its method, of course, it calls the method in the memory where it is located. Therefore, it is logical to output "Father Say hello".

  As many people think, in the above code, we know that pFather actually points to the object of the Son class, and we hope that the output result is the Say method of the son class, then to achieve this result, we need to use a virtual function .

  The result of the previous output is because the compiler has already determined the address of the function called by the object when compiling. To solve this problem , late binding is used. When the compiler uses late binding, it will be added at runtime. To determine the type of the object and call the function correctly, and to make the compiler adopt late binding, use the virtual keyword when declaring the function in the base class. Such a function is called a virtual function . Once a function Declared as virtual in the base class, then the function is virtual in all derived classes, and does not need to be explicitly declared as virtual.

  Change the code a little bit and see the running result
  

#include "stdafx.h"
#include <iostream> 
#include <stdlib.h>
using namespace std; 

class Father
{
public:
    void Face()
    {
        cout << "Father's face" << endl;
    }

    virtual void Say()
    {
        cout << "Father say hello" << endl;
    }
};


class Son:public Father
{
public:     
    void Say()
    {
        cout << "Son say hello" << endl;
    }
};

void main()
{
    Son son;
    Father *pFather=&son; // 隐式类型转换
    pFather->Say();
}

Output result:

Son say hello

  We found that the result is "Son say hello" which is calling the correct function based on the type of the object, so what happens behind the scenes when we declare Say() as virtual.

  When the compiler is compiling, it finds that there are virtual functions in the Father class. At this time, the compiler will create a virtual table (ie vtable) for each class containing virtual functions. The table is a one-dimensional array, which is stored in this array. the address of each virtual function ,

write picture description here

  
  So how to locate the virtual table? The compiler also provides a virtual table pointer (ie vptr) for each object. This pointer points to the virtual table of the class to which the object belongs. When the program is running, the vptr is initialized according to the type of the object, so that the vptr points correctly. The virtual table of the class to which it belongs, so that the correct function can be found when the virtual function is called. For the second code program, since the object type actually pointed to by pFather is Son, the vtable of the Son class pointed to by vptr is called when pFather- When >Son(), the Say() function of the Son class is found according to the function address in the virtual table.

  It is precisely because the virtual function called by each object is indexed by the virtual table pointer, which determines that the correct initialization of the virtual table pointer is very important. In other words, before the virtual table pointer is not properly initialized, we do not Can call virtual functions, so when or where is the virtual table pointer initialized ?

  The answer is to create a virtual table and initialize the virtual table pointer in the constructor. When constructing a subclass object, the constructor of the parent class must be called first. At this time, the compiler only "sees" the parent class and does not know the back Whether there is an inheritor, it initializes the virtual table pointer of the parent class object, the virtual table pointer points to the virtual table of the parent class, when the constructor of the subclass is executed, the virtual table pointer of the subclass object is initialized and points to its own virtual table. surface.


Summary (the base class has virtual functions):

  • Each class has a virtual table

  • The virtual table can be inherited. If the subclass does not rewrite the virtual function, then the subclass virtual table will still have the address of the function, but this address points to the virtual function implementation of the base class. If the base class has 3 virtual functions , then there are three items (virtual function addresses) in the virtual table of the base class, and the derived class will also have a virtual table, with at least three items. If the corresponding virtual function is rewritten, the address in the virtual table will change, pointing to its own Virtual function implementation, if the derived class has its own virtual function, the entry will be added to the virtual table.

  • The order of virtual addresses in the virtual table of the derived class is the same as the order of virtual function addresses in the virtual table of the base class.

      
      This is polymorphism in C++. When the C++ compiler is compiling, it finds that the Say() function of the Father class is a virtual function. At this time, C++ will use late binding technology, that is, the specific call is not determined at compile time. function, but at runtime, it confirms which function is called according to the type of the object. This ability is called polymorphism in C++. When we did not add the virtual keyword before the Say() function, the C++ compiler It determines which function is called, this is called early binding.

      C++ polymorphism is achieved through late binding technology .

      The polymorphism of C++ can be summed up in one sentence: add the virtual keyword before the function of the base class, and rewrite the function in the derived class. The runtime will call the corresponding function according to the actual type of the object. If the object type If it is a derived class, call the function of the derived class, if the object type is the base class, call the function of the base class.

      A virtual function is defined in the base class in order to determine the specific behavior of its derived class, for example:

      Define a base class: class Animal //animal, its function is breathe()

      Define another class class Fish //Fish. Its function is also breathe()

      Define another class class Sheep //Sheep, its function is also breathe()

      Define Fish and Sheep as derived classes of Animal. However, Fish and Sheep's breathe are different. One is breathing through water in water, and the other is breathing directly, so the base class cannot determine how to define breathe, so in the base class only A virtual breathe is defined, which is an empty virtual function. The specific functions are defined in the subclasses respectively. When the program is generally running, it finds the class, if it has a base class, then finds its base class, and finally runs the base class. The function in the class, at this time, it finds the function identified by virtual in the base class, it will go back to the subclass to find the function of the same name, the derived class is also called the subclass, the base class is also called the parent class, this is The generation of virtual functions, and the embodiment of class polymorphism.

      Polymorphism here refers to the polymorphism of a class.

      Function polymorphism is when a function is defined as a function with multiple different parameters. When you call this function, a different function of the same name is called.
      

      In general (without involving virtual functions), when we call a function with a pointer/reference, the function being called depends on the type of the pointer/reference.

      When it comes to polymorphism, virtual functions and dynamic binding are used, where the call is not determined at compile time but at runtime . Instead of considering the type of pointer/reference alone, the type of the object referred to by the pointer/reference is used to judge the call of the function, and which function to call
      
     
     is For an example of dynamism, see the output: 

#include "stdafx.h"
#include <iostream> 
#include <stdlib.h>
using namespace std; 

class CA 
{ 
public: 
    void f() 
    { 
        cout << "CA f()" << endl; 
    } 
    virtual void ff() 
    { 
        cout << "CA ff()" << endl; 
        f(); 
    } 
}; 

class CB : public CA 
{ 
public : 
    virtual void f() 
    { 
        cout << "CB f()" << endl; 
    } 
    void ff() 
    { 
        cout << "CB ff()" << endl; 
        f(); 
        CA::ff(); 
    } 
}; 
class CC : public CB 
{ 
public: 
    virtual void f() 
    { 
        cout << "C f()" << endl; 
    } 
}; 

int main() 
{ 
    CB b; 
    CA *ap = &b; 
    CC c; 
    CB &br = c; 
    CB *bp = &c; 

    ap->f(); 
    cout << endl;

    b.f(); 
    cout << endl;

    br.f(); 
    cout << endl;

    bp->f(); 
    cout << endl;

    ap->ff(); 
    cout << endl;

    bp->ff(); 
    cout << endl;

    return 0; 
}

write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326268062&siteId=291194637