C + + class and object polymorphism object-oriented programming

Posted by birdie on Mon, 31 Jan 2022 11:43:30 +0100

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;
}

Topics: C++ Back-end