C + + preliminary core Programming Chapter 5: polymorphism and virtual function classes

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:

  1. Cannot instantiate object
  2. 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:

  1. You can solve the problem of releasing subclass objects from parent class pointers

  2. 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...

Keywords: C++ Back-end

Added by rigi2 on Wed, 16 Feb 2022 12:22:30 +0200