C++ Study Notes 19-Polymorphism

19.1 Basic concepts of polymorphism

19.1.1 Static polymorphism and dynamic polymorphism

Polymorphism is one of the three major object-oriented features of C++ (encapsulation, inheritance, polymorphism).
Polymorphism is divided into two categories:

  • Static polymorphism: Function overloading and operator overloading belong to static polymorphism, and function names are reused.
  • Dynamic Polymorphism: Derived classes and virtual functions implement runtime polymorphism.

The difference between static polymorphism and dynamic polymorphism:

  • The function address of static polymorphism is bound early - the function address is determined during the compilation phase.
  • Late binding of function address in dynamic polymorphism—the function address is determined during the running phase.

The following explains polymorphism through a case:

#include<iostream>
using namespace std; 
//多态

//动物类

//地址早绑定,在编译阶段就确定了函数地址
class Animal
{
    
    
public:
	void speak()  
	{
    
    
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal
{
    
    
public:
	void speak()
	{
    
    
		cout << "小猫在说话" << endl;
		m_A = 100;
	}
	int m_A;
};

//执行说话的函数

void doSpeak(Animal& animal) //Animal& animal = cat; 传入的是子类,用父类接收,此时此对象转换为了父类
{
    
    
	cout << (typeid(animal).name()) << endl;
	animal.speak();
}

//如果想要执行让猫说话,那么这个函数地址就不能早绑定,需要在运行阶段进行绑定,也就是晚绑定

//动态多态
//1.有继承关系
//2.子类要重写父类的虚函数

//动态多态的使用
//使用动态的指针或者引用来执行子类对象
class Animal_02
{
    
    
public:
	 virtual void speak()  //虚函数
	{
    
    
		cout << "动物在说话" << endl;
	}
};

class Cat_02 :public Animal_02
{
    
    
public:
	//函数重写 函数返回值类型 函数名 参数列表 完全不同  子类中的virtual可写可不写
	void speak()
	{
    
    
		cout << "小猫在说话" << endl;
	}
};
class Dog_02 :public Animal_02
{
    
    
public:
	void speak()
	{
    
    
		cout << "小狗在说话" << endl;
	}
};

void doSpeak_02(Animal_02& animal)    //这里仍旧是子类,因为父类中有虚函数,所以在这里
{
    
    
	cout << (typeid(animal).name()) << endl;
	animal.speak();
	animal.Animal_02::speak();
}

void test1_01()
{
    
    
	Cat cat1;
	doSpeak(cat1);

	Cat_02 cat2;
	doSpeak_02(cat2);

	Dog_02 dog;
	doSpeak_02(dog);
//通过晚绑定,使得函数地址不是一开始就绑定完毕, 而是通过不同的对象指针来绑定不同的函数地址。
}

int main()
{
    
    	
	test1_01();
	system("pause");
	return 0;
}

Summary:
Satisfying conditions for dynamic polymorphism:

  1. There is an inheritance relationship.
  2. The subclass must override the virtual function of the parent class.

The use of dynamic polymorphism:
   
 use the parent class pointer or reference to execute the subclass object.

19.1.2 Principle Analysis of Polymorphism

Taking the above code as an example, when the virtual function is written, some changes will occur in the class.

In the Animal_02 class, a virtual function pointer —vfptr (virtual function pointer) is stored, which points to a virtual function table —vftable (virtual function table), and the address of the virtual function is recorded inside the virtual function table.

The way to store a function in the virtual function table is to store the scope of this function together, and what is stored is its address, namely: &Animal_02::speak.
insert image description here

When the subclass (cat_02) inherits the parent class (Animal_02), the subclass inherits the virtual function pointer and virtual function table of the parent class.
at this time:

  1. If the subclass does not override the virtual function of the parent class, then the virtual function pointer and virtual function table of the subclass are exactly the same as those of the parent class;
    insert image description here

  2. If the subclass overrides the virtual function of the parent class, the function rewritten by the subclass will overwrite the virtual function of the parent class in the virtual function table of the subclass.
    insert image description here

Note: The above is the analysis of the principle of polymorphism. In practical applications, when a virtual function appears in the parent class, no matter whether the subclass overrides the virtual function, it can be found through typeid(object).name() that even if the parent class pointer / The reference points to a subclass object whose type is also that of the subclass, rather than being changed to the superclass type as usual.
That is to say, when the parent class has a virtual function, even if the parent class pointer/reference points to the code of the subclass object , the object is still of the subclass type at this time, and can be used to access the subclass and The syntax of the parent class .

Polymorphism has functions that inheritance does not have:
If a member function (assume the name is called A) is defined in the parent class to call the virtual function (assume the name is B), then according to our principle, we use the subclass to inherit the parent class and rewrite virtual function B, and then when the subclass object calls the member function A of the parent class object, the virtual function B called by this A has been overwritten by the function rewritten by the subclass, so the overridden function B will be called directly . That is to say, we don't need to write a new A in the subclass to call the new B, just call the function A in the parent class directly through this subclass object.
In this way, we don't need to rewrite many functions that are called repeatedly in subclasses
.


19.2 Polymorphism Case 1 - Calculator

Case description:
  Using ordinary writing and polymorphic technology, respectively, design and implement a calculator class that performs operations on two operands.

Advantages of polymorphism:

  • Code organization is clear (abstract classes and subclasses are clear and readable).
  • Strong readability (every function is in a class for easy reading).
  • It is conducive to the expansion and maintenance of the early and late stages (just create or modify a new subclass directly).
#include<iostream>
using namespace std;

//普通写法
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;
		}
		//如果想扩展新的功能,需要修改源码,需要不断增加源码
		//在真实的开发中,提倡一种 开闭原则
		//开闭原则: 对扩展进行开发,对修改进行关闭
	}

	//两个操作数
	int m_Num1;
	int m_Num2;
};

void test2_01()
{
    
    
	cout << "普通实现" << endl;
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 20;
	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;
}


//利用多态实现计算器
//实现计算器抽象类
class AbstractCalculator
{
    
    
public:
	virtual int getResult() = 0;

	int m_Num1;
	int m_Num2;
};
class AddCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 + m_Num2;
	}
};
class SubCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 - m_Num2;
	}
};
class MulCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 * m_Num2;
	}
};

void test2_02()
{
    
    
	cout << "多态实现" << endl;
	//多态使用条件
	//父类指针或者引用指向子类对象

	//加法
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 20;
	cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;

	abc = new SubCalculator;  //重新规定指针指向即可
	abc->m_Num1 = 10;
	abc->m_Num2 = 20;
	cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;

	abc = new MulCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 20;
	cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;
}

int main()
{
    
    
	test2_01();
	test2_02();
	system("pause");
	return 0;
}

19.3 Pure virtual functions and abstract classes

In polymorphism, the implementation of virtual functions in the parent class is usually meaningless, mainly calling the content rewritten by the subclass.

Therefore, virtual functions can be changed to pure virtual functions .

Pure virtual function syntax:virtual 返回值类型 函数名(参数列表) = 0;

When there are pure virtual functions in a class, this class is also called an abstract class .

Abstract class features :

  • Unable to instantiate object.

  • The subclass must rewrite the pure virtual function in the abstract class, otherwise it also belongs to the abstract class.

Example:

#include<iostream>
using namespace std;
class Base
{
    
    
public:
	//只要有一个纯虚函数,这个类就称为抽象类
	virtual void func() = 0;
};
class Son :public Base
{
    
    
public:
	int m_A;
};
class Son_02 :public Base
{
    
    
public:
	void func()
	{
    
    
		cout << "func的调用" << endl;
	}
	int m_A;
};
int main()
{
    
    
	//Base b;			   //无法实例化对象
	//Base* b = new Base;  //堆区也是不可以的
	//Son s;			   //子类不重写父类纯虚函数时,也会被认为是抽象类
	Son_02 s;              //重写虚函数后,可以实例化
	Base* base = new Son_02;
	base->func();
	system("pause");
}

19.4 Polymorphic Case 2 - Making Drinks

Case description:

The general process of making drinks is: boiling water - brewing - pouring into a cup - adding auxiliary materials.

Use polymorphic technology to realize this case: provide an abstract base class for making beverages, and provide subclasses for making coffee and tea.
insert image description here

Example:

#include<iostream>
using namespace std;

class AbstractDrinking
{
    
    
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//倒入杯中
	virtual void PourInCup() = 0;
	//加入辅料
	virtual void PutSomething() = 0;
	//制作饮品
	void makeDrink()
	{
    
    
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};
//咖啡
class Coffee :public AbstractDrinking
{
    
    
public:
	//煮水
	void Boil()
	{
    
    
		cout << "煮农夫山泉" << endl;
	}
	//冲泡
	void Brew()
	{
    
    
		cout << "冲泡咖啡" << endl;
	}
	//倒入杯中
	void PourInCup()
	{
    
    
		cout << "倒入杯中" << endl;
	}
	//加入辅料
	void PutSomething()
	{
    
    
		cout << "加入糖和牛奶" << endl;
	}
	
};
//茶叶
class Tea :public AbstractDrinking
{
    
    
public:
	//煮水
	void Boil()
	{
    
    
		cout << "煮开水" << endl;
	}
	//冲泡
	void Brew()
	{
    
    
		cout << "冲泡茶叶" << endl;
	}
	//倒入杯中
	void PourInCup()
	{
    
    
		cout << "倒入杯中" << endl;
	}
	//加入辅料
	void PutSomething()
	{
    
    
		cout << "加入枸杞和柠檬" << endl;
	}
};
void dowork(AbstractDrinking* abs)
{
    
    
	abs->makeDrink();
	delete abs;
}
void test4_01()
{
    
    
	cout << "制作咖啡:" << endl;
	dowork(new Coffee);
	cout << "制作茶叶:" << endl;
	dowork(new Tea);
}
int main()
{
    
    
	test4_01();
	system("pause");
	return 0;
}

19.5 Virtual and pure virtual destruction

Problem: When using polymorphism, if there are properties in the subclass that are allocated to the heap area, then the parent class pointer cannot call the destructor code of the subclass when it is released. Because when the destructor is not a virtual function, the destructor does not have a virtual function pointer, and in the case of polymorphism, the function is called through the virtual function pointer, so the entire destructor chain will not be called.

Solution: Change the destructor in the parent class to virtual destructor or pure virtual destructor .

Common features of virtual function and pure virtual function destructor:

  • It can solve the parent class pointer to release the subclass object, that is, the destructor of the subclass can be called.
  • Both need to have specific function implementations, and pure virtual destructors also need to have function implementations, just use internal and external definitions.

The difference between virtual destructor and pure virtual destructor:

  • If it is a pure virtual destructor, the class is an abstract class and objects cannot be instantiated.

Virtual destructor syntax:

virtual ~类名(){
    
    }

Pure virtual destructor syntax:

//类内声明
virtual ~类名() = 0;
//类外定义
类名::~类名(){
    
    }

Example:

#include<iostream>
using namespace std;
class Animal5
{
    
    
public:
	Animal5()
	{
    
    
		cout << "Animal5构造函数调用" << endl;
	}
	//虚析构
	//virtual ~Animal5()
	//{
    
    
	//	cout << "Animal5析构函数调用" << endl;
	//}
	//纯虚析构
	virtual ~Animal5() = 0;

	//纯虚函数
	virtual void speak() = 0;	
};
Animal5::~Animal5()
{
    
    
	cout << "Animal5析构函数调用" << endl;
}
class Cat5 :public Animal5
{
    
    
public:
	Cat5(string name)
	{
    
    
		cout << "Cat5构造函数" << endl;
		m_Name = new string(name);
	}
	virtual void speak()
	{
    
    
		cout << *m_Name << "小猫在说话" << endl;
	}
	~Cat5()
	{
    
    
		cout << "Cat5析构函数" << endl;
		if (m_Name != NULL)
		{
    
    
			delete m_Name;
			m_Name = NULL;
		}
	}
	string* m_Name;   //创建在堆区
};

void test5_01()
{
    
    
	Animal5* animal = new Cat5("汤姆");
	animal->speak();
	delete animal;
}
int main()
{
    
    
	test5_01();
	system("pause");
	return 0;
}

Summarize:

  • Virtual destructor or pure virtual destructor is used to solve the problem of freeing subclass objects through parent class pointers.
  • If there is no heap area data in the subclass, it may not be written as virtual destructor or pure virtual destructor.
  • A class with a pure virtual destructor is also an abstract class and cannot be instantiated.

19.6 Polymorphism Case 3 - Computer Assembly

The main components of a computer are the CPU (for computing), the graphics card (for display), and the memory stick (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 that provides the functions that make the computer work, and calls the interface that each part works on.
Three different computers were assembled for work during the test.

Example:

#include<iostream>
using namespace std;

class CPU
{
    
    
public:
	//抽象计算函数
	virtual void calculate() = 0;
};
class VideoCard
{
    
    
public:
	//抽象显示函数
	virtual void display() = 0;
};
class Memory
{
    
    
public:
	//抽象存储函数
	virtual void storage() = 0;
};

//传入零件的型号,使得电脑工作。
class Computer
{
    
    
public:
	Computer(CPU* cpu, VideoCard* vc, Memory* mem)
	{
    
    
		this->cpu = cpu;
		this->vc = vc;
		this->mem = mem;
	}
	void work()
	{
    
    
		cpu->calculate();
		vc->display();
		mem->storage();
	}
	~Computer()
	{
    
    
		if (cpu != NULL)
		{
    
    
			delete cpu;
			cpu = 0;
		}
		if (vc != NULL)
		{
    
    
			delete vc;
			vc = 0;
		}
		if (mem != NULL)
		{
    
    
			delete mem;
			mem = 0;
		}
	}
private:
	CPU* cpu;
	VideoCard* vc;
	Memory* mem;
};
//Inter
class InterCPU :public CPU
{
    
    
public:
	void calculate()
	{
    
    
		cout << "Intel的CPU开始计算了" << endl;
	}
};
class InterVideoCrad :public VideoCard
{
    
    
public:
	void display()
	{
    
    
		cout << "Inter的显卡开始显示了" << endl;
	}
};
class InterMemory :public Memory
{
    
    
public:
	void storage()
	{
    
    
		cout << "Inter的内存条开始存储了" << endl;
	}
};
//Lenovo
class LenovoCPU :public CPU
{
    
    
public:
	void calculate()
	{
    
    
		cout << "Lenovo的CPU开始计算了" << endl;
	}
};
class LenovoVideoCrad :public VideoCard
{
    
    
public:
	void display()
	{
    
    
		cout << "Lenovo的显卡开始显示了" << endl;
	}
};
class LenovoMemory :public Memory
{
    
    
public:
	void storage()
	{
    
    
		cout << "Lenovo的内存条开始存储了" << endl;
	}
};

void test6_01()
{
    
    
	//创建第一台电脑
	cout << "第一台电脑开始工作" << endl;
	CPU* intercpu = new InterCPU;
	VideoCard* intercard = new InterVideoCrad;
	Memory* intermemory = new InterMemory;
	Computer* computer1 = new Computer(intercpu, intercard, intermemory);
	computer1->work();
	delete computer1;

	//创建第二台电脑
	cout <<"-----------------\n" << "第二台电脑开始工作" << endl;
	CPU* lenovocpu = new LenovoCPU;
	VideoCard* lenovocard = new LenovoVideoCrad;
	Memory* lenovomemory = new LenovoMemory;
	Computer* computer2 = new Computer(lenovocpu, lenovocard, lenovomemory);
	computer2->work();
	delete computer2;

	//创建第三台电脑
	cout << "-----------------\n" << "第三台电脑开始工作" << endl;
	Computer* computer3 = new Computer(new LenovoCPU, new InterVideoCrad, new LenovoMemory);
	computer3->work();
	delete computer3;

}
int main()
{
    
    
	test6_01();
	system("pause");
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_49030008/article/details/123354311