C + + polymorphic and virtual functions quick start tutorial

Posted by cdrees on Thu, 10 Feb 2022 10:05:43 +0100

C + + polymorphic and virtual functions quick start tutorial

As mentioned in the previous chapter, the pointer of the base class can also point to the derived class object. Please see the following example:

#include <iostream>
using namespace std;
//Base class People
class People{
public:
    People(char *name, int age);
    void display();
protected:
    char *m_name;
    int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
    cout<<m_name<<"this year"<<m_age<<"Years old, a vagrant without a job."<<endl;
}
//Derived class Teacher
class Teacher: public People{
public:
    Teacher(char *name, int age, int salary);
    void display();
private:
    int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
    cout<<m_name<<"this year"<<m_age<<"Years old, is a teacher, every month"<<m_salary<<"Yuan of income."<<endl;
}
int main(){
    People *p = new People("Wang Gang", 23);
    p -> display();
    p = new Teacher("Zhao Jia", 45, 8200);
    p -> display();
    return 0;
}

Operation results:

Wang Gang is 23 years old. He is an unemployed vagrant.
Zhao Jia is 45 years old. He is a vagrant without a job.

We intuitively believe that if the pointer points to the derived class object, we should use the member variables and member functions of the derived class, which is in line with people's thinking habits. However, the running result of this example tells us that when the base class pointer p points to the object of the derived class Teacher, although the member variable of Teacher is used, its member function is not used, resulting in different output results (Zhao Jia was originally a Teacher, but the output result shows that he is a homeless person), which is not in line with our expectations.

In other words, only the member variables of the derived class can be accessed through the base class pointer, but the member functions of the derived class cannot be accessed.

In order to eliminate this embarrassment and allow the base class pointer to access the member functions of derived classes, C + + adds virtual functions. Using virtual functions is very simple. You only need to add the virtual keyword in front of the function declaration.

Change the above code and declare display() as a virtual function:

#include <iostream>
using namespace std;
//Base class People
class People{
public:
    People(char *name, int age);
    virtual void display();  //Declare as virtual function
protected:
    char *m_name;
    int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
    cout<<m_name<<"this year"<<m_age<<"Years old, a vagrant without a job."<<endl;
}
//Derived class Teacher
class Teacher: public People{
public:
    Teacher(char *name, int age, int salary);
    virtual void display();  //Declare as virtual function
private:
    int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
    cout<<m_name<<"this year"<<m_age<<"Years old, is a teacher, every month"<<m_salary<<"Yuan of income."<<endl;
}
int main(){
    People *p = new People("Wang Gang", 23);
    p -> display();
    p = new Teacher("Zhao Jia", 45, 8200);
    p -> display();
    return 0;
}

Operation results:

Wang Gang is 23 years old. He is an unemployed vagrant.
Zhao Jia, 45, is a teacher with a monthly income of 8200 yuan.

Compared with the previous example, this example only adds a virtual keyword before the declaration of the display() function, and declares the member function as a Virtual Function, so that the member function of the Teacher class can be called through the p pointer. The running results also prove this (Zhao Jia is already a Teacher and no longer a homeless person).

With virtual functions, when the base class pointer points to the base class object, it uses the members of the base class (including member functions and member variables), and when it points to the derived class object, it uses the members of the derived class. In other words, the base class pointer can do things in the way of the base class or the derived class. It has many forms or manifestations. We call this phenomenon Polymorphism.

In the above code, it is also p - > display(); This statement performs different operations when p points to different objects. The same statement can perform different operations and appear to have different manifestations, which is polymorphism.

Polymorphism is one of the main features of object-oriented programming. The only use of virtual functions in C + + is to form polymorphism.

The purpose of C + + providing polymorphism is to provide "all-round" access to the member variables and member functions of all derived classes (including direct and indirect derivation) through the base class pointer, especially the member functions. If there is no polymorphism, we can only access member variables.

As we said earlier, when calling an ordinary member function through a pointer, we will judge which class member function to call according to the type of the pointer (the pointer defined by which class). However, through the analysis of this section, we can find that this statement does not apply to virtual functions, which are called according to the pointer, The virtual function of which class is called when the pointer points to the object of which class.

But then again, the memory model of the object is very clean and does not contain any information about member functions. What exactly does the compiler find member functions based on? We will give the answer in the next section.

Polymorphism can also be achieved by reference

Reference is essentially realized by pointer. Since polymorphism can be realized by pointer, we have reason to infer that polymorphism can also be realized by reference.

Modify the internal code of the main() function in the above example and replace the pointer with a reference:

int main(){
    People p("Zhi Gang Wang", 23);
    Teacher t("Zhao Hongjia", 45, 8200);
   
    People &rp = p;
    People &rt = t;
   
    rp.display();
    rt.display();
    return 0;
}

Operation results:

Wang Zhigang, 23, is a vagrant.
Zhao Hongjia, 45, is a teacher with a monthly income of 8200 yuan.

Because reference is similar to constant, it can only be initialized at the same time of definition, and it must be consistent in the future, and other data can no longer be referenced. Therefore, in this example, two reference variables must be defined, one to reference the base class object and the other to reference the derived class object. From the running results, it can be seen that when the reference of the base class refers to the base class object, the member of the base class is called, while when it refers to the derived class object, the member of the derived class is called.

However, references are not as flexible as pointers. Pointers can change the direction at any time, while references can only refer to fixed objects and lack expressiveness in polymorphism. Therefore, when we talk about polymorphism in the future, we generally mean pointers. The main purpose of this example is to let readers know that in addition to pointers, references can also be polymorphic.

Use of polymorphism

Through the above examples, readers may not have found the use of polymorphism, but it is true that polymorphism has little use in small projects.

In the following example, let's assume that you are playing a military game and the enemy suddenly launches a ground war, so you order the army, air force and all their active equipment to enter the operational state. The specific codes are as follows:

#include <iostream>
using namespace std;
//army
class Troops{
public:
    virtual void fight(){ cout<<"Strike back!"<<endl; }
};
//land force
class Army: public Troops{
public:
    void fight(){ cout<<"--Army is fighting!"<<endl; }
};
//99A main battle tank
class _99A: public Army{
public:
    void fight(){ cout<<"----99A(Tank) is fighting!"<<endl; }
};
//Wuzhi 10 armed helicopter
class WZ_10: public Army{
public:
    void fight(){ cout<<"----WZ-10(Helicopter) is fighting!"<<endl; }
};
//Changjian 10 cruise missile
class CJ_10: public Army{
public:
    void fight(){ cout<<"----CJ-10(Missile) is fighting!"<<endl; }
};
//air force
class AirForce: public Troops{
public:
    void fight(){ cout<<"--AirForce is fighting!"<<endl; }
};
//J-20 stealth fighter
class J_20: public AirForce{
public:
    void fight(){ cout<<"----J-20(Fighter Plane) is fighting!"<<endl; }
};
//CH5 UAV
class CH_5: public AirForce{
public:
    void fight(){ cout<<"----CH-5(UAV) is fighting!"<<endl; }
};
//Bomb 6K bomber
class H_6K: public AirForce{
public:
    void fight(){ cout<<"----H-6K(Bomber) is fighting!"<<endl; }
};
int main(){
    Troops *p = new Troops;
    p ->fight();
    //land force
    p = new Army;
    p ->fight();
    p = new _99A;
    p -> fight();
    p = new WZ_10;
    p -> fight();
    p = new CJ_10;
    p -> fight();
    //air force
    p = new AirForce;
    p -> fight();
    p = new J_20;
    p -> fight();
    p = new CH_5;
    p -> fight();
    p = new H_6K;
    p -> fight();
    return 0;
}

Operation results:

Strike back!
--Army is fighting!
----99A(Tank) is fighting!
----WZ-10(Helicopter) is fighting!
----CJ-10(Missile) is fighting!
--AirForce is fighting!
----J-20(Fighter Plane) is fighting!
----CH-5(UAV) is fighting!
----H-6K(Bomber) is fighting!

There are many derived classes in this example. If polymorphism is not used, multiple pointer variables need to be defined, which is easy to cause confusion; With polymorphism, only one pointer variable p can call the virtual functions of all derived classes.

From this example, we can also find that for large and medium-sized programs with complex inheritance, polymorphism can increase their flexibility and make the code more expressive.

Topics: C++ Back-end