https://www.bilibili.com/video/BV1et411b73Z?p=135https://www.bilibili.com/video/BV1et411b73Z?p=135 Polymorphism is one of the three characteristics of C + + object-oriented
Polymorphisms fall into two categories:
Static polymorphism: function overloading and operator overloading belong to static polymorphism and reuse function names
Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism
Difference between static polymorphism and dynamic polymorphism:
Statically polymorphic function address early binding - the function address is determined in the compilation stage
Dynamic polymorphic function address late binding - the function address is determined in the operation stage
1. Basic syntax of polymorphism
Dynamic polymorphism meets the following conditions:
1. There is an inheritance relationship
2. The subclass overrides the virtual function of the parent class
#include <iostream> using namespace std; //Dynamic polymorphism meets the following conditions: //1. There is an inheritance relationship //2. The subclass overrides the virtual function of the parent class //The return value type of rewriting function is exactly the same as the parameter list of function name //Dynamic polymorphism usage: // A pointer or reference to a parent class points to a child class object //Animals class Animal { public: virtual void speak() { cout << "Animals are speaking!" << endl; } virtual void spoken() { cout << "Animals were speaking!" << endl; } }; class Cat:public Animal { public: void speak() { cout << "Cats are speaking!" << endl; } }; class Dog :public Animal { public: void speak() { cout << "Dogs are speaking!" << endl; } }; void doSpeak(Animal & animal)//C + + allows type conversion between parent and child { animal.speak(); } void test01() { Cat cat; doSpeak(cat); Dog dog; doSpeak(dog); } void test02() { cout << "size of Animal = " << sizeof(Animal) << endl; } int main() { test01(); test02(); return 0; }
2. Principle analysis of polymorphism
#include <iostream> using namespace std; //Dynamic polymorphism meets the following conditions: //1. There is an inheritance relationship //2. The subclass overrides the virtual function of the parent class //The return value type of rewriting function is exactly the same as the parameter list of function name //Dynamic polymorphism usage: // A pointer or reference to a parent class points to a child class object //Animals class Animal { public: virtual void speak() { cout << "Animals are speaking!" << endl; } virtual void spoken() { cout << "Animals were speaking!" << endl; } }; class Cat:public Animal { public: void speak() { cout << "Cats are speaking!" << endl; } }; class Dog :public Animal { public: void speak() { cout << "Dogs are speaking!" << endl; } }; void doSpeak(Animal & animal)//C + + allows type conversion between parent and child { animal.speak(); } void test01() { Cat cat; doSpeak(cat); Dog dog; doSpeak(dog); } void test02() { cout << "size of Animal = " << sizeof(Animal) << endl; } int main() { test01(); test02(); return 0; }
3. Case 1 - calculator
Case description:
Using common writing method and polymorphic technology, a calculator class is designed to realize the operation of two operands
Advantages of polymorphism:
Clear code organization
Strong readability
It is conducive to early and later maintenance and expansion
Common implementation:
//ordinary class Calculator { public: int getResult(string oper) { if (oper == "+") { return m_Num1 + m_Num2; } else if (oper == "-") { return m_Num1 - m_Num2; } else if (oper == "*") { return m_Num1 * m_Num2; } //If you want to expand new functional requirements, modify the source code //Advocate the principle of opening and closing in real development //Opening and closing principle: develop extensions and close modifications } int m_Num1;//Operand 1 int m_Num2;//Operand 2 };
Polymorphic implementation:
#include <iostream> #include <string> using namespace std; //The calculator is realized by using common writing method and polymorphic technology respectively //ordinary class Calculator { public: int getResult(string oper) { if (oper == "+") { return m_Num1 + m_Num2; } else if (oper == "-") { return m_Num1 - m_Num2; } else if (oper == "*") { return m_Num1 * m_Num2; } //If you want to expand new functional requirements, modify the source code //Advocate the principle of opening and closing in real development //Opening and closing principle: develop extensions and close modifications } int m_Num1;//Operand 1 int m_Num2;//Operand 2 }; //Using polymorphism to realize calculator //Benefits of polymorphism // 1. Clear organizational structure // 2. Strong readability // 3. For early and late expansion and high maintainability //Implement computer abstract classes class AbstractCalculator { public: virtual int getResult() { return 0; } int m_Num1;//Operand 1 int m_Num2;//Operand 2 }; //Addition calculation class class AddCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 + m_Num2; } }; //Subtraction calculation class class SubCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 - m_Num2; } }; //Multiplication calculation class class MulCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 * m_Num2; } }; void test02() { //Polymorphic service conditions //A parent class pointer or reference points to a child class object //Addition operation AbstractCalculator* abc = new AddCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; //Remember to destroy it after use delete abc; //Subtraction operation abc = new SubCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; //Multiplication abc = new MulCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; } void test01() { //Create computer object Calculator c; c.m_Num1 = 10; c.m_Num2 = 10; cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl; cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl; cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl; } int main() { //test01(); test02(); return 0; }
4. Pure virtual functions and abstract classes
In polymorphism, the implementation of the virtual function of the parent class is meaningless, mainly by calling the content rewritten by the subclass
Therefore, virtual functions can be changed to pure virtual functions
Pure virtual function syntax: 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
Abstract class features:
Cannot instantiate object
Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
#include <iostream> using namespace std; class Base { public: //Pure virtual function // As long as there is a pure virtual function, this class is called an abstract class // Abstract class characteristics // 1. Cannot instantiate object // 2. The subclass of an abstract class must override the pure virtual function in the parent class, otherwise it also belongs to an abstract class // virtual void func() = 0; }; class Son :public Base { public: virtual void func() { cout << "func function call" << endl; }; }; void test01() { /*Base b; Abstract classes cannot instantiate objects new Base;*/ Son s;//The subclass must override the pure virtual function in the parent class, otherwise the object cannot be instantiated Base* base = new Son; base->func(); } int main() { test01(); return 0; }
5. Case 2 - making drinks
#include <iostream> using namespace std; //Case 2 making drinks class AbstractDrinking { public: //boil water virtual void Boil() = 0; //Brew virtual void Brew() = 0; //PourInCup virtual void PourInCup() = 0; //Add excipients virtual void PutSomething() = 0; //Making drinks void makeDrink() { Boil(); Brew(); PourInCup(); PutSomething(); } }; //Making coffee class Coffee :public AbstractDrinking { //boil water virtual void Boil() { cout << "Boiled farmer spring" << endl; } //Brew virtual void Brew() { cout << "Brew coffee" << endl; } //PourInCup virtual void PourInCup() { cout << "Pour into the coffee cup" << endl; } //Add excipients virtual void PutSomething() { cout << "Add sugar and milk" << endl; } //Making drinks }; //Making tea class Tea :public AbstractDrinking { //boil water virtual void Boil() { cout << "Boiled mineral water" << endl; } //Brew virtual void Brew() { cout << "Brewing tea" << endl; } //PourInCup virtual void PourInCup() { cout << "Pour into a teacup" << endl; } //Add excipients virtual void PutSomething() { cout << "Add medlar" << endl; } //Making drinks }; void doWork(AbstractDrinking* abs) { abs->makeDrink();//Interface delete abs;//release } void test01() { //Making coffee doWork(new Coffee); cout << "---------------------" << endl; doWork(new Tea); } int main() { test01(); return 0; }
6. 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, the object belongs to an abstract class and cannot be instantiated
Virtual destructor syntax: virtual ~ class name () {}
Pure virtual destructor syntax: virtual ~ class name () = 0;
Class name:: ~ class name () {}
#include <iostream> #include <iostream> using namespace std; //Virtual destruct and pure virtual destruct class Animal { public: //Pure virtual function virtual void speak() = 0; Animal() { cout << "Animal Constructor call for" << endl; } Using virtual destructor can solve the problem of unclean parent class pointer release //virtual ~Animal() //{ // Cout < < virtual destructor call of animal < < endl; //} //Pure virtual destructors need to be declared and implemented virtual ~Animal() = 0; }; Animal::~Animal() { cout << "Animal Pure virtual destructor call" << endl; } class Cat :public Animal { public: Cat(string name) { cout << "Cat Constructor call for" << endl; m_Name = new string(name); } ~Cat() { if (m_Name != NULL) { cout << "Cat Destructor call" << endl; delete m_Name; m_Name = NULL; } } virtual void speak() { cout << *m_Name << "The kitten is talking" << endl; } string* m_Name; }; void test01() { Animal * animal = new Cat("Tom"); animal->speak(); //When the parent class pointer is destructed, it will not call the destructor in the subclass, resulting in memory leakage if the subclass has heap attributes delete animal; } int main() { test01(); return 0; }