Polymorphism mechanism in c + +

Catalog

1. Background

2. Introduction to polymorphism

2-1 what is polymorphism

Classification of 2-2 polymorphism

2-3 conditions of dynamic polymorphism

2-4 static and dynamic coupling

2-5 realization principle of dynamic polymorphism

2-6 virtual destructor

2.7 thinking about virtual function

2.8 pure virtual function, abstract class, interface

Background introduction

Virtual function Rewriting: the subclass redefines the virtual function with the same return value, name and parameter in the parent class;

Non virtual function Rewriting: the subclass redefines the non virtual function with the same name and parameters in the parent class;

Assignment compatibility between parents and children: child objects can be used as parent objects (compatibility); the specific performance is as follows:

1. Subclass objects can be directly assigned to parent objects;

2. The subclass object can directly initialize the parent object;

3. The parent class pointer can directly point to the child class object;

4. The parent class reference can directly reference the child class object;

When assignment compatibility occurs, the subclass object degenerates into the parent object, which can only access the members defined in the parent class, and can directly access the members with the same name covered by the subclass;

 1 // In the assignment compatibility principle, the subclass object degenerates into the parent object, and the subclass is a special parent;
 2 #include <iostream>
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 class Parent
 8 {
 9 public:
10     int mi;
11 
12     void add(int i)
13     {
14         mi += i;
15     }
16     
17     void add(int a, int b)
18     {
19         mi += (a + b);
20     }
21 };
22 
23 class Child : public Parent
24 {
25 public:
26     int mi;
27     
28     void add(int x, int y, int z)
29     {
30         mi += (x + y + z);
31     }
32 };
33 
34 int main()
35 {
36     Parent p;
37     Child c;
38     
39     c.mi = 100;
40     p = c;             // p.mi = 0; Subclass objects degenerate to parent objects
41     Parent p1(c);   // p1.mi = 0; Ditto
42     Parent& rp = c;
43     Parent* pp = &c;
44     
45     rp.add(5);             
46     pp->add(10, 20);        
47     
48     cout << "p.mi: " << p.mi <<endl;                           // p.mi: 0; 
49     cout << "p1.mi: " << p1.mi <<endl;                       // p1.mi: 0;  
50     cout << "c.Parent::mi: " << c.Parent::mi <<endl;    // c.Parent::mi: 35
51     cout << "rp.mi: " << rp.mi <<endl;                        // rp.mi: 35
52     cout << "pp->mi: " << pp->mi <<endl;                 // pp->mi: 35
53     
54     return 0;
55 }
Assignment compatibility test

In the object-oriented inheritance relationship, we know that the subclass can have all the properties and behaviors in the parent class; however, sometimes the methods provided in the parent class cannot meet the existing needs, so we must rewrite the existing methods in the parent class in the subclass to meet the current needs. At this time, although we have implemented function rewriting (here is non virtual function rewriting), we can not expect the result in the type compatibility principle (we can not adjust to the corresponding rewriting function according to the actual object type pointed by the pointer / reference). Next, we use code to reproduce this scenario:

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Parent
 7 {
 8 public:    
 9     void print()
10     {
11         cout << "I'm Parent." << endl;
12     }
13 };
14 
15 class Child : public Parent
16 {
17 public:    
18     void print()
19     {
20         cout << "I'm Child." << endl;
21     }
22 };
23 
24 void how_to_print(Parent* p)
25 {
26     p->print();
27 }
28 
29 int main()
30 {
31     Parent p;
32     Child c;
33     
34     how_to_print(&p);   // I'm Parent    // Expected to print: I'm Parent.
35     how_to_print(&c);   // I'm Parent    // Expected to print: I'm Child.
36     
37     return 0;
38 }
On the compatibility of non virtual function rewriting and assignment

Why does this happen? (in assignment compatibility, why can't a subclass override function be called when a parent class pointer / reference points to a subclass object?)

Problem analysis: during compilation, the compiler can only judge the object pointed to according to the type of pointer; according to the compatibility of assignment, the compiler thinks that the pointer of the parent class points to the object of the parent class; therefore, the compilation result can only call the function with the same name defined in the parent class.

       

When compiling this function, the compiler cannot know exactly what the pointer p points to. However, there is no reason for the compiler to report an error, so the compiler thinks that the safest way is to call the print function of the parent class. Because both the parent and the child must have the same print function.

To solve this problem, we need to use polymorphism in c + +. So how to realize polymorphism in c + +? Please see the following details:

Polymorphism introduction

1. What is polymorphism

In real life, polymorphism is a variety of forms of the same thing in different scenes.

In object-oriented, polymorphism refers to the behavior of dynamically calling the actual bound object function at runtime through the pointer or reference of the base class. The corresponding compile time binding function is called static binding.

Polymorphism is the basis of design pattern, and polymorphism is the basis of framework.

2. Classification of polymorphism

  

Static polymorphism is completed by the compiler during compilation. The compiler will choose to call the appropriate function according to the type of arguments. If there is a suitable function, it will call, otherwise it will give a warning or error;

Dynamic polymorphism is a virtual function that determines which class to call according to the object pointed by the reference (pointer) of the base class when the program is running.

3. The conditions of dynamic polymorphism

According to previous problems, the compiler does not meet our expectations (because the compiler calls the function that is rewritten in the parent class according to the type of the parent class pointer). However, in the object-oriented polymorphism, we expect the behavior to determine how to call the rewrite function (virtual function) according to the actual object type.

1. When the parent class pointer (Reference) points to the parent class object, the virtual function defined in the parent class is called;

2. When the parent class pointer (Reference) points to the child class object, the virtual function defined in the child class is called;

       

The performance effect of this kind of polymorphic behavior is: the same call statement has many different performance forms in actual operation.

So in c + +, how to achieve this performance effect? (conditions for polymorphism)

1. Inheritance

2. There should be virtual function rewriting (the function declared by virtual is called virtual function)

3. There should be a parent class pointer (parent class reference) to the child class object

4. Static and dynamic binding

Static binding: specific function calls can be determined during program compilation, such as function overloading and non virtual function rewriting;

Dynamic binding: the specific function call can be determined only after the program is actually running, such as virtual function rewriting, switch statement and if statement;

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Parent
 7 {
 8 public:
 9     virtual void func()
10     {
11         cout << "Parent::void func()" << endl;
12     }
13     
14     virtual void func(int i)
15     {
16         cout << "Parent::void func(int i) : " << i << endl;
17     }
18     
19     virtual void func(int i, int j)
20     {
21         cout << "Parent::void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
22     }
23 };
24 
25 class Child : public Parent
26 {
27 public:
28     void func(int i, int j)
29     {
30         cout << "Child::void func(int i, int j) : " << i + j << endl;
31     }
32     
33     void func(int i, int j, int k)
34     {
35         cout << "Child::void func(int i, int j, int k) : " << i + j + k << endl;
36     }
37 };
38 
39 void run(Parent* p)
40 {
41     p->func(1, 2);     // Show polymorphism
42                        // Dynamic linking
43 }
44 
45 
46 int main()
47 {
48     Parent p;
49     
50     p.func();         // Static linking
51     p.func(1);        // Static linking
52     p.func(1, 2);     // Static linking
53     
54     cout << endl;
55     
56     Child c;
57     
58     c.func(1, 2);     // Static linking
59     
60     cout << endl;
61     
62     run(&p);
63     run(&c);
64     
65     return 0;
66 }
67 /*
68     Parent::void func()
69     Parent::void func(int i) : 1
70     Parent::void func(int i, int j) : (1, 2)
71 
72     Child::void func(int i, int j) : 3
73 
74     Parent::void func(int i, int j) : (1, 2)
75     Child::void func(int i, int j) : 3
76 */
Case list of static binding and dynamic binding

5. The realization principle of dynamic polymorphism

Virtual function table and vptr pointer

1. When a virtual function is declared in a class, the compiler will generate a virtual function table in the class;

2. The virtual function table is a data structure for storing function pointers of class members;

3. The virtual function table is automatically generated and maintained by the compiler;

4. Virtual member functions will be put into the virtual function table by the compiler;

5. When there are virtual functions, each object has a pointer to the virtual function table (vptr pointer).

Polymorphic execution process:

1. In a class, when a function is declared with virtual, a virtual function table will be generated in the class and stored in the table;

2. When an object is created with this class, a vptr pointer will be generated, which will point to the corresponding virtual function table;

3. When a polymorphic call is made, the vptr pointer will look up the called function in the virtual function table of the corresponding class according to the object, so as to find the entry address of the function;

If this object is a subclass object, the vptr pointer will look up the called function in the virtual function table of the subclass

If the object is the object of the parent class, the vptr pointer will look up the called function in the virtual function table of the parent class

  

  

 

 

 

Note: for efficiency reasons, it is not necessary to declare all member functions as virtual functions.

How to prove the existence of vptr pointer?

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Demo1
 7 {
 8 private:
 9     int mi; // 4 bytes
10     int mj; // 4 bytes
11 public:
12     virtual void print(){}  // Due to the existence of virtual functions, when class objects are instantiated, 1 vptr Pointer
13 };
14 
15 class Demo2
16 {
17 private:
18     int mi; // 4 bytes
19     int mj; // 4 bytes
20 public:
21     void print(){}
22 };
23 
24 int main()
25 {
26     cout << "sizeof(Demo1) = " << sizeof(Demo1) << " bytes" << endl; // sizeof(Demo1) = 16 bytes
27     cout << "sizeof(Demo2) = " << sizeof(Demo2) << " bytes" << endl; // sizeof(Demo2) = 8 bytes
28     
29     return 0;
30 }
31 
32 // 64bit(OS) Pointers account for 8 bytes
33 // 32bit(OS) Pointers account for 4 bytes
Proof of vptr pointer

Obviously, in a normal class, the size of the class = = the size of the member variable; in a class with a virtual function, the size of the class = = the size of the member variable + the size of the vptr pointer.

6. Virtual destructor

Definition: virtual keyword is used to modify the destructor, which is called virtual destructor;

Format: virtual ~ClassName() {...}

Meaning: the virtual destructor is used to instruct the delete operator to correctly destruct the dynamic object; (when the parent class pointer points to the child class object, the memory space of all the child classes is released through the parent class pointer)

Application scenario: in the assignment compatibility principle (the parent class pointer points to the child class object), delete the parent class pointer to release the memory space of all the children. (dynamic polymorphic call: judge how to call delete operator through the actual object pointed by the parent class pointer)

!! : it is recommended to declare the destructor as a virtual function when designing the base class, in order to avoid memory leakage, otherwise it may cause memory leakage of the derived class.
Case analysis
 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 protected:
 9     char *name;
10 public:
11     Base()
12     {
13         name = new char[20];
14         strcpy(name, "Base()");
15         cout <<this << "  " << name << endl;
16     }
17     
18     ~Base()
19     {
20         cout << this << "  ~Base()" << endl;
21         delete[] name;
22     }
23 };
24 
25 
26 class Derived : public Base
27 {
28 private:
29     int *value;
30 public:
31     Derived()
32     {
33         strcpy(name, "Derived()");
34         value = new int(strlen(name));
35         cout << this << "  " << name << "  " << *value <<  endl;
36     }
37     
38     ~Derived()
39     {
40         cout << this << "  ~Derived()" << endl;
41         delete value;
42     }
43 };
44 
45 
46 int main()
47 {    
48     cout << "The test of memory leak in subclass object in assignment compatibility" << endl;
49     
50     Base* bp = new Derived();
51     cout << bp << endl;
52     // ...
53     delete bp;  // Although it is a parent class pointer, it is a child class resource
54 
55     return 0;
56 }
57 
58 /**
59  * The test of memory leak in subclass object in assignment compatibility
60  * 0x7a1030  Base()
61  * 0x7a1030  Derived()  9
62  * 0x7a1030
63  * 0x7a1030  ~Base()
64  */
In assignment compatibility, subclass memory leak case column
 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 protected:
 9     char *name;
10 public:
11     Base()
12     {
13         name = new char[20];
14         strcpy(name, "Base()");
15         cout <<this << "  " << name << endl;
16     }
17     
18     virtual ~Base()
19     {
20         cout << this << "  ~Base()" << endl;
21         delete[] name;
22     }
23 };
24 
25 
26 class Derived : public Base
27 {
28 private:
29     int *value;
30 public:
31     Derived()
32     {
33         strcpy(name, "Derived()");
34         value = new int(strlen(name));
35         cout << this << "  " << name << "  " << *value <<  endl;
36     }
37     
38     virtual ~Derived()
39     {
40         cout << this << "  ~Derived()" << endl;
41         delete value;
42     }
43 };
44 
45 
46 int main()
47 {
48     //Derived *dp = new Derived();
49     //delete dp; // Releasing resources directly through subclass objects is not required virtual Keyword
50     
51     cout << "Test of virtual destructor in assignment compatibility" << endl;
52     
53     Base* bp = new Derived();
54     cout << bp << endl;
55     // ...
56     delete bp;  // Dynamic polymorphism
57 
58     return 0;
59 }
60 
61 /**
62  * Test of virtual destructor in assignment compatibility
63  * 0x19b1030  Base()
64  * 0x19b1030  Derived()  9
65  * 0x19b1030
66  * 0x19b1030  ~Derived()
67  * 0x19b1030  ~Base()
68  */
Virtual destructor solving subclass memory leak cases

The difference between the two case columns: the first case column is a common destructor; the second case column is a virtual destructor.

7. Thinking about virtual function

1. Can a constructor be a virtual function? ---No

No. Because the virtual function table pointer will be initialized correctly after the execution of the constructor.

In the polymorphism of c + +, the virtual function table is generated and maintained automatically by the compiler, and the pointer of the virtual function table is initialized by the constructor, that is, the constructor is equivalent to the entry point of the virtual function, which is responsible for the preliminary work of calling the virtual function; during the execution of the constructor, the pointer of the virtual function table may not be properly initialized; because it is compiled in different c + + The implementation of virtual function table and virtual function table pointer is different, so it is forbidden to declare the constructor as a virtual function.

          

2. Can analytic functions become virtual functions? ---Virtual function with polymorphism

Yes, and generate dynamic polymorphism. Because the destructor is called before the object is destroyed, that is, before the object is destroyed, the virtual function table pointer points to the corresponding virtual function table correctly.

3. Can virtual functions be called in constructors to generate polymorphism? ---No polymorphism

A virtual function can be called in a constructor, but polymorphism is not possible because the virtual function table pointer is not properly initialized when the constructor executes.

4. Can virtual functions be called in destructors to generate polymorphism? ---No polymorphism

A virtual function can be called in a destructor, but polymorphism is not possible because the virtual function table pointer has been destroyed when the destructor executes.    

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 public:
 9     Base()
10     {
11         cout << "Base()" << endl;
12         
13         func();
14     }
15     
16     virtual void func() 
17     {
18         cout << "Base::func()" << endl;
19     }
20     
21     virtual ~Base()
22     {
23         func();
24         
25         cout << "~Base()" << endl;
26     }
27 };
28 
29 
30 class Derived : public Base
31 {
32 public:
33     Derived()
34     {
35         cout << "Derived()" << endl;
36         
37         func();
38     }
39     
40     virtual void func()
41     {
42         cout << "Derived::func()" << endl;
43     }
44     
45     virtual ~Derived()
46     {
47         func();
48         
49         cout << "~Derived()" << endl;
50     }
51 };
52 
53 void test()
54 {
55     Derived d;
56 }
57 
58 int main()
59 {   
60     //Stack space
61     test();
62 
63     // Heap space
64     //Base* p = new Derived();    
65     //delete p; // Polymorphism (pointer p Point to a subclass object and have a virtual function override)
66     
67     return 0;
68 }
69 /*
70 Base()
71 Base::func()
72 Derived()
73 Derived::func()
74 Derived::func()
75 ~Derived()
76 Base::func()
77 ~Base()
78 */
Calling virtual function columns in construction and analysis

Conclusion: the virtual function can not be polymorphic in the constructor and destructor, it only calls the function version defined in the current class!!

8. Pure virtual function, abstract class, interface

1. Definition - explain in case

It must be very familiar to all of you. For any ordinary class, you can instantiate multiple objects, that is, each object can be described by its corresponding class, and these objects can find their own prototypes in real life. For example, there is now a "Dog class" "" , we can use this "dog" "" Many "dogs" are produced by examples " However, in the object-oriented analysis, we will find some abstract concepts, which describe a kind of things and can not reflect a concrete object. We call this phenomenon containing abstract concepts as abstract classes. There are many examples about abstract classes, such as: how to calculate the area of a "figure"; what "pet" is the most lovely and so on. After learning about abstract classes, what are pure virtual functions? Now we will explain the problem with the abstract class case of how to calculate the area of a "figure"; in this example, there are two abstract concepts, namely "figure" and "area", that is, what kind of "figure" -- I don't know how to "calculate the area" or "area formula"--- I don't know. Here, we can regard "graph" as the class name of abstract class, and "area" as the member function of abstract class, because this member function can't be implemented, just let the outside know that there is such a thing, the member function here can be regarded as a pure virtual function, and at the same time, the abstract class here can also be regarded as an interface.

2. Features

Pure virtual function:

(1) only virtual functions are declared in the base class, and the function body does not need to be defined in the base class. The syntax format is: virtual void funtion1()=0;

(2) "= 0" means that the compiler is currently declaring a pure virtual function, so there is no need to define the function body.

(3) pure virtual function becomes virtual function after being realized;

(4) the pure virtual function in the base class is an interface. The pure virtual function cannot be called. It exists only to re implement the method in the derived class;

(5) c + + stipulates that the virtual destructor must include declaration and Implementation (before the object is destroyed, the destructor in the base class is called last, if there is no corresponding function implementation at this time, it is obviously not possible);

Abstract class:

(1) used to represent abstract concepts in the real world;

(2) it is a kind of class that can only define type but not create object; however, it can have abstract class pointer or interface class pointer, and polymorphism will occur when it points to subclass object;

(3) the abstract class can only be inherited as the parent class, and the child class must realize the concrete function of pure virtual function;

(4) there is no concept of abstract class in c + +, but it can be realized by pure virtual function;

(5) the existence of pure virtual function in a c + + class becomes an abstract class; (judgment condition)

(6) if a subclass does not implement a pure virtual function, the subclass becomes an abstract class.

Interface:

(1) there is no member variable defined in the class;

(2) all member functions are public pure virtual functions; (judgment condition 1 + 2)

(3) interface is a special abstract class;

A class full of pure virtual functions is an interface;

A class part is pure virtual function is abstract class;

3. Introduction reason

(1) in order to use polymorphism, we often need to declare pure virtual function in base class.

(2) in many cases, it is unreasonable for the base class itself to generate objects. For example, as a base class, animals can derive tiger, peacock and other subclasses, but the objects generated by animals themselves are obviously unreasonable.

Therefore, in order to solve the above problems, the concept of pure virtual function is introduced; if the member function of the base class is declared as pure virtual function, the compiler requires that the member function must be rewritten in the derived class to achieve polymorphism.

 1 #include <iostream>
 2 #include <typeinfo>
 3 
 4 using namespace std;
 5 
 6 class Shape
 7 {
 8 public:
 9     virtual double area() = 0;
10 };
11 
12 class Rect : public Shape
13 {
14     int ma;
15     int mb;
16 public:
17     Rect(int a, int b)
18     {
19         ma = a;
20         mb = b;
21     }
22     double area()
23     {
24         return ma * mb;
25     }
26 };
27 
28 class Circle : public Shape
29 {
30     int mr;
31 public:
32     Circle(int r)
33     {
34         mr = r;
35     }
36     double area()
37     {
38         return 3.14 * mr * mr;
39     }
40 };
41 
42 void area(Shape* p)
43 {
44     const type_info &tis = typeid(*p);
45     
46     if( tis == typeid(Rect) )
47     {
48         Rect *rect = dynamic_cast<Rect*>(p);
49         
50         cout << "the area of the Rect : " << rect->area() << endl;
51     }
52     
53     if( tis == typeid(Circle) )
54     {
55         Circle *circle = dynamic_cast<Circle*>(p);
56         
57         cout << "the area of the Circle : " << circle->area() << endl;
58     }
59     
60 }
61 
62 int main()
63 {
64     Rect rect(1, 2);
65     Circle circle(10);
66     
67     area(&rect);
68     area(&circle);
69     
70     return 0;
71 }
72 /**
73  * Operation result:
74  * the area of the Rect : 2
75  * the area of the Circle : 314
76  */
How to use abstract class to calculate the area of a "figure"

 

Keywords: C++

Added by bibby on Mon, 06 Apr 2020 03:02:24 +0300