C++ polymorphism | principles of polymorphism | virtual functions

Article directory

Table of contents

1. The concept of polymorphism

1. Concept

2. Definition and implementation of polymorphism

1.Conditions for polymorphism

2.Virtual function

3. Rewriting of virtual functions

Two exceptions to virtual function overriding:

4.override and final in c++11

5. Comparison of overloading, overwriting (rewriting), hiding (redefining)

3. Abstract class

1. Concept

2. Interface inheritance and implementation inheritance

4. The principle of polymorphism

1. Virtual function table

2. The principle of polymorphism

3. Dynamic binding and static binding

5. Virtual function table in single inheritance and multiple inheritance

1. Virtual function table in single inheritance

2. Virtual function table in multiple inheritance

6. Common interview questions about inheritance and polymorphism



1. The concept of polymorphism

1. Concept

Completing a certain behavior will produce different states when different objects complete it.

For example: when ordinary people buy tickets , they buy them at full price; when students buy tickets, they buy them at half price; military personnel buy them at half price.

When buying tickets, buy tickets first

2. Definition and implementation of polymorphism

1.Conditions for polymorphism

Polymorphism is when class objects with different inheritance relationships call the same function, resulting in different behaviors. For example, Student inherits Person. The Person object buys full-price tickets, and the Student object buys half-price tickets.

Two conditions that constitute polymorphism in inheritance:

1. Virtual functions must be called through pointers or references of the base class

2. The called function must be a virtual function, and the derived class must override the virtual function of the base class.

Rewriting: The function name is the same, the parameter types are the same, and the return value is the same

2.Virtual function

Virtual function: A class member function modified by virtual is called a virtual function

class Person
{
    public:
        virtual void BuyTicker()
        {
        }
};

3. Rewriting of virtual functions

Rewriting (overwriting) of virtual functions: There is a virtual function in the derived class that is exactly the same as the base class (that is, the return value type, function name, and parameter list of the derived class virtual function and the base class virtual function are exactly the same), which is called a subclass The virtual function overrides the virtual function of the base class

class Person
{
    public:
         virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person 
{
    public:
         virtual void BuyTicket() { cout << "买票-半价" << endl; }
};


void Func(Person& p)
{
    p.BuyTicket();
}

int main()
{
    Person p;
    Student s;
    Fun(p);
    Fun(s);
    return 0;
}
Exception: When overriding a base class virtual function, the virtual function of the derived class can also constitute an override without adding the virtual keyword ( because after inheritance, the virtual function of the base class is inherited and remains virtual in the derived class). function attribute ), but this writing method is not very standardized and is not recommended to be used in this way.

Two exceptions to virtual function overriding:

1. Covariance (the return value type of the virtual function of the base class and the derived class is different) must be a parent-child relationship pointer or reference

class Person
{
    public:
        virtual Person* BuyTicket()
        {
            cout<<"all"<<endl;
            return this;
        }

};


class Student :public Person
{
    public:
            virtual Student* BuyTicket()
            {
                cout<<"half"<<endl;
                return this;
            }
};

2. Rewriting of destructors (the names of destructors of base classes and derived classes are different)

If the destructor of the base class is a virtual function, as long as the destructor of the derived class is defined, regardless of whether the virtual keyword is added, it will be overridden with the destructor of the base class. Although the destructor names of the base class and the derived class Different, it seems to violate the principle of rewriting, but it is not. It can be understood that the compiler has done special processing for the name of the destructor, and the name of the compiled destructor is unified into destructor

4.override and final in c++11

1.final: Modify the virtual function to indicate that the virtual function cannot be rewritten.

class Car
{
   public:
        vitual void Dirve() final
    {}
};

class Benz:public car
{
    virtual void Drive()
    {
            cout<<"benz-"<<endl;
    }
};

2.overrider: Check whether the derived class virtual function has overridden a certain virtual function. If not, an error will be reported.

class Car
{
public:
 virtual void Drive(){}
};


class Benz :public Car
{
public:
 virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

5. Comparison of overloading, overwriting (rewriting), hiding (redefining)

Overloading : two functions in the same scope , with the same function name and different parameters

Rewriting (overwriting): The two functions are in the scope of the base class and the derived class respectively. The function names/parameters/return values ​​must be the same (covariance exceptions), and the two functions must be virtual functions .

Redefinition (hidden): The two functions are in the scope of the base class and the derived class respectively. The function names are the same. The functions of the two base classes and the derived class with the same name do not constitute overwriting or redefinition.

3. Abstract class

1. Concept

Write =0 after the virtual function, then the function is a pure virtual function. The class containing the pure virtual function is called an abstract class (also called an interface class). Abstract classes cannot instantiate objects, and derived classes cannot inherit them. To instantiate an object, only by rewriting a pure virtual function can a derived class instantiate an object. Pure virtual functions specify that derived classes must be rewritten. In addition, pure virtual functions also reflect interface inheritance.

2. Interface inheritance and implementation inheritance

Inheritance of ordinary functions is a kind of implementation inheritance. The derived class inherits the base class function and can use the function. What is inherited is the implementation of the function.

Inheritance of virtual functions is a kind of interface inheritance. The derived class inherits the interface of the base class virtual function. The purpose is to override and achieve polymorphism. What is inherited is the interface, so if you do not implement polymorphism, do not define the function as virtual function.

4. The principle of polymorphism

// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
 virtual void Func1()
 {
 cout << "Func1()" << endl;
 }
private:
 int _b = 1;  //4
 char _c = 0;  //1
};


sizeof(Base) //12

In addition to the two members, there is also an additional _vfptr placed in front of the object (some platforms may be placed behind the object). This pointer in the object is called the virtual function table pointer (v-virtual f-function). A pointer containing virtual There is at least one virtual function table pointer in the function class, because the address of the virtual function is placed in the virtual function table, and the virtual function table is also referred to as the virtual table.

1. Virtual function table

class Base
{
    public:
     virtual void Func1()
     {
     cout << "Base::Func1()" << endl;
     }
     virtual void Func2()
     {
     cout << "Base::Func2()" << endl;
     }
    
    //普通函数
     void Func3()
     {
     cout << "Base::Func3()" << endl;
     }
     private:
         int _b = 1;
};

class Derive : public Base
{
public:
     //实现了虚函数的重写
     virtual void Func1()
     {
        Person::BuyTicket,传Student调用的是Student::BuyTicket
        cout << "Derive::Func1()" << endl;
     }
private:
     int _d = 2;
};



int main()
{
     Base b;
     Derive d;
     return 0;
}
  • There is also a virtual table pointer in the derived class object d. The d object is composed of two parts. One part is the member inherited from the parent class. The virtual table pointer is the other part's own member of the existing part.
  • The base class object b and the derived class object d have different virtual tables. Fun1 completes the rewriting, so the virtual table in d stores the rewritten Drive::Func1, so the rewriting of the virtual function is also called overwriting. Coverage refers to the coverage of virtual functions in the virtual table. Rewriting is the name of syntax, and overwriting is the name of remote layer.
  • Func2 inherits a virtual function, so it is put into the virtual table. Fun3 also inherits it, but it is not a virtual function, so it is not put into the virtual table.
  • The virtual function table is essentially a pointer array that stores virtual function pointers. Generally, a nullptr is placed at the end of this array.
  • Generate a virtual table for a derived class: a. First copy the contents of the virtual table in the base class to the virtual table of the derived class. b. If the derived class overrides a virtual function in the base class, use the derived class’s own The virtual function covers the virtual function of the base class in the virtual table c. The virtual functions newly added by the derived class are added to the end of the derived class's virtual table in the order of declaration in the derived class.
  • The virtual function is in the virtual table, and the virtual table is in the object (❌) should be : virtual function pointers are stored in the virtual table , not virtual functions. The virtual function is stored in the code segment , and its pointer is stored in the virtual table . What is stored in the object is not a virtual table, but a virtual table pointer . Virtual tables are also stored in code segments .
  • When slicing, only the object is copied, not the virtual table.
    int main()
    {
        Student johnson;
        Func(johnson);
    
        Person mike = johnson;
    
        return 0;
    }

2. The principle of polymorphism

It can be seen that the function call after satisfying polymorphism is not determined at compile time, but is retrieved from the object after running. Function calls that do not satisfy polymorphism are confirmed at compile time .

3. Dynamic binding and static binding

  • Static binding is also called early binding (early binding). It determines the severity of the program during program compilation. It is also called static polymorphism, such as function overloading.
  • Dynamic binding is also called late binding. During the running of the program, the specific behavior of the program is determined based on the type obtained and specific functions are called. It is also called dynamic polymorphism.

5. Virtual function table in single inheritance and multiple inheritance

1. Virtual function table in single inheritance

class Base
 { 
public :
 virtual void func1() { cout<<"Base::func1" <<endl;}
 virtual void func2() {cout<<"Base::func2" <<endl;}
private :
 int a;
};


class Derive :public Base 
{ 
public :
 virtual void func1() {cout<<"Derive::func1" <<endl;}
 virtual void func3() {cout<<"Derive::func3" <<endl;}
 virtual void func4() {cout<<"Derive::func4" <<endl;}
private :
 int b;
};

//使用程序来打印虚表

typedef void(*VF_PTR)();   //定义一个函数指针vf_ptr
void printfVTable(VF_PTR table[])
{
    cout<<"虚表地址:" <<table<<endl;
    for(int i = 0; table[i] ! = nullptr ; ++i)
    {
        printf("%d -%p",i,table[i]);
        VF_PTR f = table[i];
        f();
    }

    cout<<endl;
};

    int main()
    {
        Base b; 
        Drive d;
        //思路: 取出b,d对象中的前4个字节,就是虚表的指针,这个虚函数表本质是一个存虚函数地址的指针数组,这个数组最后放了一个Nullptr
        //先取对象的地址,然后强转为int * 的指针
        //再解引用取值,就取到了b对象中的前4个字节,这个值就是虚表的指针
        //再强转为vf_ptr,因为虚表就是一个vf_ptr类型的数组
        //虚表指针传递给函数进行打印
        VF_PTR * vTable = (VF_PTR*)(*(int *)&b);
        printfVTable(vTable);
         VF_PTR * vTable = (VF_PTR*)(*(int *)&d);
         printfVTable(vTable);
    }

2. Virtual function table in multiple inheritance

class Base1 {
public:
 virtual void func1() {cout << "Base1::func1" << endl;}
 virtual void func2() {cout << "Base1::func2" << endl;}
private:
 int b1;
};
class Base2 {
public:
 virtual void func1() {cout << "Base2::func1" << endl;}
 virtual void func2() {cout << "Base2::func2" << endl;}
private:
 int b2;
};
class Derive : public Base1, public Base2 {
public:
 virtual void func1() {cout << "Derive::func1" << endl;}
 virtual void func3() {cout << "Derive::func3" << endl;}
private:
 int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
 cout << " 虚表地址>" << vTable << endl;
 for (int i = 0; vTable[i] != nullptr; ++i)
 {
 printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
 VFPTR f = vTable[i];
 f();
 }
 cout << endl;
}
int main()
{
 Derive d;
 VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
 PrintVTable(vTableb1);
 VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d+sizeof(Base1)));
 PrintVTable(vTableb2);
 return 0;
}

Derive inherits two classes, so there are two virtual tables. I implemented a func3 myself, and I saw from the memory that the unrewritten virtual functions of the multi-inherited derived class are placed in the virtual function table of the first inherited base class part.

6. Common interview questions about inheritance and polymorphism

  1. Can an inline function be a virtual function? Answer: Yes, but the compiler ignores the inline attribute, and this function is no longer inline, because the virtual function needs to be placed in the virtual table.
  2.  Can static members be virtual functions? Answer: No, because the static member function does not have this pointer, and the virtual function table cannot be accessed using the calling method of type::member function, so the static member function cannot be put into the virtual function table.
  3. Can a constructor be a virtual function? Answer: No, because the virtual function table pointer in the object is initialized during the constructor initialization list stage.
  4.  Can a destructor be a virtual function? In what scenarios is the destructor a virtual function? Answer: Yes, and it is best to define the destructor of the base class as a virtual function. Refer to the content of this courseware
  5. Is it faster for objects to access normal functions or virtual functions? Answer: First of all, if it is a normal object, it is just as fast. If it is a pointer object or a reference object, the ordinary function called is faster. Because it constitutes polymorphism, calling a virtual function at runtime requires searching in the virtual function table.
  6. At what stage is the virtual function table generated and where does it exist? Answer: The virtual function table is generated during the compilation phase, and generally exists in the code segment (constant area).
  7. What is an abstract class? What is the role of abstract classes? Answer: A class with pure virtual functions is called an abstract class (add =0 after the virtual function). Abstract classes force overriding of virtual functions, and abstract classes reflect interface inheritance relationships.
  8. Which of the following object-oriented methods can make you rich (A)
  9. A: Inheritance B: Encapsulation C: Polymorphism D: Abstraction
  10. (D) is a mechanism in object-oriented programming languages. This mechanism realizes that the definition of the method has nothing to do with the specific object, and the call to the method can be associated with the specific object.
  11. A: Inheritance B: Template C: Self-reference of object D: Dynamic binding
  12.  Regarding inheritance and composition in object-oriented design, which of the following statements is wrong? (C)
  13. A: Inheritance allows us to override and rewrite the implementation details of the parent class. The implementation of the parent class is visible to the child class. It is a kind of static reuse , also called white box reuse.
  14. B: The combined objects do not need to care about their respective implementation details. The relationship between them is determined at runtime. It is a kind of dynamic reuse, also called black box reuse.
  15. C: Prioritizing inheritance over composition is the second principle of object-oriented design.
  16. D: Inheritance allows subclasses to automatically inherit the interface of the parent class, but in design patterns this is considered to undermine the encapsulation
  17. Which of the following statements about pure virtual functions is correct (A)
  18. A: A class that declares a pure virtual function cannot instantiate an object. B: A class that declares a pure virtual function is a virtual base class.
  19. C: The subclass must implement the pure virtual function of the base class D: The pure virtual function must be an empty function
  20. The correct description of virtual functions is (B)
  21. A: The virtual function of the derived class and the virtual function of the base class have different number of parameters and types B: Inline functions cannot be virtual functions
  22. C: The derived class must redefine the virtual function of the base class D: The virtual function can be a static function
  23. 6. The correct statement about virtual tables is (B )
  24. A: A class can only have one virtual table
  25. B: There are virtual functions in the base class. If the subclass does not override the virtual function of the base class, the subclass and the base class share the same virtual table.
  26. C: The virtual table is dynamically generated during runtime
  27. D: Different objects of a class share the virtual table of the class
  28.  Suppose there is a virtual function in class A, B inherits from A, B overrides the virtual function in A, and does not define any virtual function, then (D)
  29. A: The first 4 bytes of class A objects store the virtual table address, and the first 4 bytes of class B objects are not the virtual table address.
  30. B: The first 4 bytes of class A objects and class B objects store the address of the virtual base table.
  31. C: The virtual table addresses stored in the first 4 bytes of class A objects and class B objects are the same.
  32. D: The number of virtual functions in the virtual tables of classes A and B is the same, but classes A and B do not use the same virtual table.

Guess you like

Origin blog.csdn.net/jolly0514/article/details/132863420