C + + preliminary core Programming Chapter 5: polymorphism and virtual function classes
1. Summary of polymorphism
Polymorphism literally means multiple forms. Polymorphism is used when there is a hierarchy between classes and classes are associated through inheritance. C + + polymorphism means that when calling member functions, different functions will be executed according to the type of object calling the function.
In the following example, the base class Shape is derived into two classes, as follows:
#include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // Main function of program int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // Stores the address of the rectangle shape = &rec; // Call the area function area of rectangle shape->area(); // Storage address of triangle shape = &tri; // Call the area function area of triangle shape->area(); return 0; }
Operation results:
Parent class area : Parent class area :
The reason for the wrong output is that the calling function area() is set to the version in the base class by the compiler. This is the so-called static polymorphism, or static link - the function call is ready before the program execution. Sometimes this is also called early binding because the area() function is already set during program compilation.
But now, let's modify the program slightly and put the keyword virtual before the declaration of area() in the Shape class, as shown below:
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } virtual int area() { cout << "Parent class area :" <<endl; return 0; } };
Operation results:
Rectangle class area : Triangle class area :
2. Polymorphic case I - calculator
//Polymorphic implementation //Abstract calculator class //Advantages of polymorphism: the code organization structure is clear and readable, which is conducive to early and later expansion and maintenance class AbstractCalculator { public : virtual int getResult() { return 0; } int m_Num1; int m_Num2; }; //Addition calculator class AddCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 + m_Num2; } }; //Subtraction calculator class SubCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 - m_Num2; } }; //Multiplication calculator class MulCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 * m_Num2; } }; void test02() { //Create addition calculator AbstractCalculator *abc = new AddCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; //When you run out, remember to destroy it //Create subtraction calculator abc = new SubCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; //Create multiplication calculator abc = new MulCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; } int main() { //test01(); test02(); system("pause"); return 0; }
3. Pure virtual functions and abstract classes
In polymorphism, the implementation of virtual functions in the parent class is meaningless, which is mainly the content rewritten by calling subclasses. Therefore, the virtual function can be changed to pure virtual function. The syntax of pure virtual function: virtual return value type function name (parameter list) = 0; When there are pure virtual functions in a class, this class is also called an abstract class
Characteristics of abstract classes:
- Cannot instantiate object
- Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
class Base { public: //Pure virtual function //As long as there is a pure virtual function in a class, it is called an abstract class virtual void func() = 0; }; class Son :public Base { public: virtual void func() { cout << "func call" << endl; }; }; void test01() { Base * base = NULL; //base = new Base; // Error, abstract class cannot instantiate object base = new Son; base->func(); delete base;//Remember to destroy } int main() { test01(); system("pause"); return 0; }
4. Case 2 - making drinks
Case description:
The general process of making drinks is as follows: boiling water - brewing - pouring into the cup - adding auxiliary materials, using polymorphic technology to realize this case, providing Abstract beverage base classes and subclasses to make coffee and tea.
//Abstract making drinks class AbstractDrinking { public: //Boil water virtual void Boil() = 0; //Brew virtual void Brew() = 0; //Pour into a cup virtual void PourInCup() = 0; //Add excipients virtual void PutSomething() = 0; //Specified process void MakeDrink() { Boil(); Brew(); PourInCup(); PutSomething(); } }; //Making coffee class Coffee : public AbstractDrinking { public: //Boil water virtual void Boil() { cout << "Boiled farmer spring!" << endl; } //Brew virtual void Brew() { cout << "Brew coffee!" << endl; } //Pour into a cup virtual void PourInCup() { cout << "Pour the coffee into the cup!" << endl; } //Add excipients virtual void PutSomething() { cout << "Add milk!" << endl; } }; //Making tea class Tea : public AbstractDrinking { public: //Boil water virtual void Boil() { cout << "Boiled tap water!" << endl; } //Brew virtual void Brew() { cout << "Brewing tea!" << endl; } //Pour into a cup virtual void PourInCup() { cout << "Pour the tea into the cup!" << endl; } //Add excipients virtual void PutSomething() { cout << "Add medlar!" << endl; } }; //Business function void DoWork(AbstractDrinking* drink) { drink->MakeDrink(); delete drink; } void test01() { DoWork(new Coffee); cout << "--------------" << endl; DoWork(new Tea); } int main() { test01(); system("pause"); return 0; }
5. Virtual deconstruction and pure virtual deconstruction
When polymorphism is used, if an attribute in a subclass is opened to the heap, the parent class pointer cannot call the destructor code of the subclass when it is released
Solution: change the destructor in the parent class to virtual destructor or pure virtual destructor
The commonness between virtual destruct and pure virtual destruct:
-
You can solve the problem of releasing subclass objects from parent class pointers
-
All need specific function implementation
Difference between virtual destruct and pure virtual destruct:
If it is a pure virtual destructor, this class belongs to an abstract class and cannot instantiate an object
Virtual destructor syntax:
virtual ~ class name () {}
Pure virtual destructor syntax:
virtual ~ class name () = 0;
Class name:: ~ class name () {}
class Animal { public: Animal() { cout << "Animal Constructor call!" << endl; } virtual void Speak() = 0; //The destructor plus the virtual keyword becomes a virtual destructor //virtual ~Animal() //{ // Cout < < "animal virtual destructor call!"<< endl; //} virtual ~Animal() = 0; }; Animal::~Animal() { cout << "Animal Pure virtual destructor call!" << endl; } //Like a class containing ordinary pure virtual functions, a class containing pure virtual destructors is also an abstract class. Cannot be instantiated. class Cat : public Animal { public: Cat(string name) { cout << "Cat Constructor call!" << endl; m_Name = new string(name); } virtual void Speak() { cout << *m_Name << "The kitten is talking!" << endl; } ~Cat() { cout << "Cat Destructor call!" << endl; if (this->m_Name != NULL) { delete m_Name; m_Name = NULL; } } public: string *m_Name; }; void test01() { Animal *animal = new Cat("Tom"); animal->Speak(); //Releasing through the parent class pointer may cause the subclass objects to be unclearly cleaned, resulting in memory leakage //How? Add a virtual destructor to the base class //The virtual destructor is used to release subclass objects through the parent class pointer delete animal; } int main() { test01(); system("pause"); return 0; }
Virtual destruct or pure virtual destruct is used to release subclass objects through the parent class pointer. If there is no heap data in the subclass, it can not be written as virtual destruct or pure virtual destruct
6. Case 3 - computer assembly
Case description:
The main components of the computer are CPU (for calculation), graphics card (for display) and memory module (for storage)
Encapsulate each part into an abstract base class, and provide different manufacturers to produce different parts, such as Intel manufacturers and Lenovo manufacturers
Create a computer class, provide functions that let the computer work, and call the interface of each part
During the test, three different computers were assembled to work
#include<iostream> using namespace std; //Abstract CPU class class CPU { public: //Abstract computing function virtual void calculate() = 0; }; //Abstract graphics card class class VideoCard { public: //Abstract display function virtual void display() = 0; }; //Abstract memory module class class Memory { public: //Abstract storage function virtual void storage() = 0; }; //Computer class Computer { public: Computer(CPU * cpu, VideoCard * vc, Memory * mem) { m_cpu = cpu; m_vc = vc; m_mem = mem; } //Functions that provide work void work() { //Make the part work and call the interface m_cpu->calculate(); m_vc->display(); m_mem->storage(); } //Provide destructor to release 3 computer parts ~Computer() { //Release CPU parts if (m_cpu != NULL) { delete m_cpu; m_cpu = NULL; } //Release graphics card parts if (m_vc != NULL) { delete m_vc; m_vc = NULL; } //Release memory module parts if (m_mem != NULL) { delete m_mem; m_mem = NULL; } } private: CPU * m_cpu; //CPU pointer part VideoCard * m_vc; //Graphics card part pointer Memory * m_mem; //Memory module part pointer }; //Specific manufacturer //Intel manufacturer class IntelCPU :public CPU { public: virtual void calculate() { cout << "Intel of CPU Start counting!" << endl; } }; class IntelVideoCard :public VideoCard { public: virtual void display() { cout << "Intel Your graphics card is starting to show!" << endl; } }; class IntelMemory :public Memory { public: virtual void storage() { cout << "Intel My memory module is beginning to store!" << endl; } }; //Lenovo manufacturer class LenovoCPU :public CPU { public: virtual void calculate() { cout << "Lenovo of CPU Start counting!" << endl; } }; class LenovoVideoCard :public VideoCard { public: virtual void display() { cout << "Lenovo Your graphics card is starting to show!" << endl; } }; class LenovoMemory :public Memory { public: virtual void storage() { cout << "Lenovo My memory module is beginning to store!" << endl; } }; void test01() { //First computer parts CPU * intelCpu = new IntelCPU; VideoCard * intelCard = new IntelVideoCard; Memory * intelMem = new IntelMemory; cout << "The first computer starts working:" << endl; //Create first computer Computer * computer1 = new Computer(intelCpu, intelCard, intelMem); computer1->work(); delete computer1; cout << "-----------------------" << endl; cout << "The second computer starts working:" << endl; //Second computer assembly Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);; computer2->work(); delete computer2; cout << "-----------------------" << endl; cout << "The third computer starts working:" << endl; //Third computer assembly Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);; computer3->work(); delete computer3; }
THE END...