C++ 核心篇 9 之类与对象 - 多态

多态的基本语法

多态分为二类:静态多态和动态多态

》》》静态多态:函数重载和运算符重载属于静态多态

》》》动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:静态多态的函数地址早绑定(编译阶段确认函数地址)动态多态的函数地址晚绑定(运行阶段确认函数地址)


程序举例

#include <iostream>
using namespace std;

class Animal {
public:
	void speak() {
		cout << "动物在说话!" << endl;
	}
};

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

void doSpeak(Animal &a) {
	a.speak();
}

int main() {
	
	Cat c1;
	c1.speak(); // 小猫在说话!
	// 多态的目的在于 Animal &a = c1; a.speak(); 的实际调用其实是 c1.speak();
	doSpeak(c1); // 动物在说话!

	system("pause");
	return 0;
}

此处我们使用虚函数实现晚绑定

#include <iostream>
using namespace std;

class Animal {
public:
	virtual void speak() {
		cout << "动物在说话!" << endl;
	}
};

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

void doSpeak(Animal &a) {
	a.speak();
}

int main() {
	
	Cat c1;
	c1.speak(); // 小猫在说话!
	doSpeak(c1); // 小猫在说话!

	system("pause");
	return 0;
}

动态多态要求满足的条件:有继承关系;子类重写父类的虚函数

多态案例1 - 计算器类

原始实现

#include <iostream>
using namespace std;
#include <string>

class Calculate {
public:
	// 构造函数
	Calculate(int num1, int num2) {
		Num1_ = num1;
		Num2_ = num2;
	}
	// 计算器的实现
	int getResult(string oper){
		if (oper == "+") {
			return Num1_ + Num2_;
		}
		else if (oper == "-") {
			return Num1_ - Num2_;
		}
		else if (oper == "*") {
			return Num1_ * Num2_;
		}
		else if (oper == "/") {
			return Num1_ / Num2_;
		}
	}
public:
	int Num1_;
	int Num2_;
};

int main() {
	
	Calculate c1(3, 4);
	cout << c1.getResult("+") << endl; // 7
	cout << c1.getResult("-") << endl; // -1
	cout << c1.getResult("*") << endl; // 12
	cout << c1.getResult("/") << endl; // 0

	system("pause");
	return 0;
}

多态实现

#include <iostream>
using namespace std;

class Calculate {
public:
	// 计算器的实现 - 虚函数
	virtual int getResult() {
		return 0;
	}

public:
	int Num1_;
	int Num2_;
};

class Add :public Calculate{
public:
	// 重写父类虚函数 - virtual 可写可不写
	virtual int getResult() {
		return Num1_ + Num2_;
	}
};

class Sub :public Calculate {
public:
	// 重写父类虚函数 - virtual 可写可不写
	virtual int getResult() {
		return Num1_ - Num2_;
	}
};

class Mul :public Calculate {
public:
	// 重写父类虚函数 - virtual 可写可不写
	virtual int getResult() {
		return Num1_ * Num2_;
	}
};

class Div :public Calculate {
public:
	// 重写父类虚函数 - virtual 可写可不写
	virtual int getResult() {
		return Num1_ / Num2_;
	}
};

int main() {
	
	// 多态使用条件:父类指针或引用指向子类对象
	// 1. 实现加法
	Calculate *cal1 = new Add;
	// 二个操作数
	cal1->Num1_ = 10;
	cal1->Num2_ = 2;
	cout << cal1->getResult() << endl; // 12

	// 2. 实现减法
	Calculate *cal2 = new Sub;
	// 二个操作数
	cal2->Num1_ = 10;
	cal2->Num2_ = 2;
	cout << cal2->getResult() << endl; // 8

	// 3. 实现乘法
	Calculate *cal3 = new Mul;
	// 二个操作数
	cal3->Num1_ = 10;
	cal3->Num2_ = 2;
	cout << cal3->getResult() << endl; // 20

	//4. 实现除法
	Calculate *cal4 = new Div;
	// 二个操作数
	cal4->Num1_ = 10;
	cal4->Num2_ = 2;
	cout << cal4->getResult() << endl; // 5

	system("pause");
	return 0;
}

优点:组织结构清晰;可读性强;对与前期和后期扩展以及维护性高

纯虚函数和抽象类

在多态中通常父类中虚函数的实现都是毫无意义的,主要调用子类中重写的内容

》》》因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;

》》》当类中存在纯虚函数,这个类也称为抽象类

抽象类特点

》》》无法实例化对象
》》》子类必须重写抽象类中的纯虚函数否则也属于抽象类

程序举例

#include <iostream>
using namespace std;

// 抽象类
class Base {
public:
	// 纯虚函数
	virtual void func() = 0;
};

// 继承自抽象类却不重写纯虚函数故也是抽象类
class Son :public Base{

};

int main() {
	
	// Base b1; // Error!
	// 不能实例化抽象类 “Base” 因为 void Base::func(void)”: 是抽象的
	// Son s1; // Error!
	// 不能实例化抽象类 “Son” 因为 void Son::func(void)”: 是抽象的

	system("pause");
	return 0;
}

多态案例2 - 学科学习过程

#include <iostream>
using namespace std;

// 抽象类
class AbstractStudying {
public:
	// 1. 调整好心态
	virtual void AjustMind() = 0;
	// 2. 学习方法
	virtual void StudyMethod() = 0;
	// 3. 认真程度
	virtual void ConcernDegree() = 0;
	// 4. 回报
	virtual void Repay() = 0;

	// 类中函数全实现
	void doWork() {
		AjustMind();
		StudyMethod();
		ConcernDegree();
		Repay();
	}
};

// 数学类
class Math :public AbstractStudying{
public:
	// 重写纯虚函数
	// 1. 调整好心态
	virtual void AjustMind() {
		cout << "数学学习心态:10 分" << endl;
	}
	// 2. 学习方法
	virtual void StudyMethod() {
		cout << "数学学习方法:10 分" << endl;
	}
	// 3. 认真程度
	virtual void ConcernDegree() {
		cout << "数学认真程度:10 分" << endl;
	}
	// 4. 回报
	virtual void Repay() {
		cout << "数学学习回报:10 分" << endl;
	}
};

// 英语类
class English :public AbstractStudying{
public:
	// 重写纯虚函数
	// 1. 调整好心态
	virtual void AjustMind() {
		cout << "英语学习心态:10 分" << endl;
	}
	// 2. 学习方法
	virtual void StudyMethod() {
		cout << "英语学习心态:10 分" << endl;
	}
	// 3. 认真程度
	virtual void ConcernDegree() {
		cout << "英语学习心态:10 分" << endl;
	}
	// 4. 回报
	virtual void Repay() {
		cout << "英语学习心态:10 分" << endl;
	}
};

int main() {
	
	// 数学学习过程
	AbstractStudying *abs1 = new Math;
	abs1->doWork();

	cout << "-------------------" << endl;

	// 英语学习过程
	AbstractStudying *abs2 = new English;
	abs2->doWork();
	
	// outputs:数学学习心态:10 分
	//		   数学学习方法:10 分
	//		   数学认真程度:10 分
	//		   数学学习回报:10 分
	//		   -------------------
	//		   英语学习心态:10 分
	//		   英语学习心态:10 分
	//		   英语学习心态:10 分
	//		   英语学习心态:10 分

	system("pause");
	return 0;
}

虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构函数,也就不能释放子类中开辟到堆区的属性

》》》解决方式:将父类中的析构函数改为虚析构或纯虚析构

虚析构和纯虚析构共性

》》》可以解决父类指针释放子类对象

》》》都需要有具体的函数实现

虚析构和纯虚析构区别

》》》如果是纯虚析构,该类属于抽象类,无法实例化对象

程序举例

#include <iostream>
using namespace std;
#include <string>

// 被纯虚析构函数修饰 -- 抽象类
class Animal {
public:
	Animal() {
		cout << "Animal 构造函数实现" << endl;
	}
	// 纯虚析构函数
	// 重点:无论是虚析构函数或是纯虚析构函数都需要在函数体有相关实现
	virtual ~Animal() = 0; // 错误实现:~Animal() = 0; 
	virtual void Speak() = 0;
};
// 在类外实现纯虚析构函数的相关实现
Animal::~Animal() {
	cout << "Animal 纯虚析构函数实现" << endl;
}

// 子类
class Cat :public Animal{
public:
	// 重写纯虚析构函数
	~Cat(){
		if (Name_ != NULL) {
			delete Name_;
			Name_ = NULL;
		}
		cout << "Cat 纯虚析构函数实现" << endl;
	}
	// 构造函数
	Cat(string name) {
		Name_ = new string(name);
		cout << "Cat 构造函数实现" << endl;
	}
	// 重写纯虚函数
	virtual void Speak() {
		cout << "小猫" << *Name_ << "在说话!" << endl;
	}
public:
	// 小猫名称
	string *Name_;
};

void func() {
	Animal *aml = new Cat("苏苏");
	// outputs:Animal 构造函数实现
	//		   Cat 构造函数实现
	aml->Speak(); // 小猫苏苏在说话!

	// 原始析构函数:父类指针在析构时不会调用子类的析构函数,导致堆区内存泄露
	// 新析构函数:也就是将父类析构函数改为虚析构函数或纯析构函数
	delete aml;
	// outputs:Cat 纯虚析构函数实现
	//		   Animal 纯虚析构函数实现
}

int main() {
	
	func();

	system("pause");
	return 0;
}

多态案例3 - 电脑组装

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

// 电脑零件产商 - Intel
class IntelCPU :public CPU{
public:
	// 重写父类中的纯虚函数
	virtual void Calculate() {
		cout << "Intel 的 CPU 开始工作了!" << endl;
	}
};
class IntelVideoCard :public VideoCard {
public:
	// 重写父类中的纯虚函数
	virtual void Display() {
		cout << "Intel 的 显卡 开始工作了!" << endl;
	}
};
class IntelMemory :public Memory {
public:
	// 重写父类中的纯虚函数
	virtual void Storage() {
		cout << "Intel 的 内存条 开始工作了!" << endl;
	}
};
// 电脑零件产商 - HP
class HPCPU :public CPU {
public:
	// 重写父类中的纯虚函数
	virtual void Calculate() {
		cout << "HP 的 CPU 开始工作了!" << endl;
	}
};
class HPVideoCard :public VideoCard {
public:
	// 重写父类中的纯虚函数
	virtual void Display() {
		cout << "HP 的 显卡 开始工作了!" << endl;
	}
};
class HPMemory :public Memory {
public:
	// 重写父类中的纯虚函数
	virtual void Storage() {
		cout << "HP 的 内存条 开始工作了!" << endl;
	}
};

// 电脑类
class Computer {
public:
	// 构造函数
	Computer(CPU *cpu, VideoCard * videocard, Memory *memory) {
		Cpu_ = cpu;
		Videocard_ = videocard;
		Memory_ = memory;
	}
	// 析构函数
	~Computer() {
		// 释放 CPU
		if (Cpu_ != NULL) {
			delete Cpu_;
			Cpu_ = NULL;
		}
		// 释放显卡
		if (Videocard_ != NULL) {
			delete Videocard_;
			Videocard_ = NULL;
		}
		// 释放内存条
		if (Memory_ != NULL) {
			delete Memory_;
			Memory_ = NULL;
		}
		cout << "电脑零件:CPU、显卡和内存条占用的堆区内存已被释放!" << endl;
	}
	// 提供工作的函数
	void Work() {
		Cpu_->Calculate();
		Videocard_->Display();
		Memory_->Storage();
	}
private:
	CPU *Cpu_; // CPU 的零件指针
	VideoCard * Videocard_; // 显卡的零件指针
	Memory *Memory_; // 内存条的零件指针
};


void func() {
	// 第一台电脑零件
	CPU *cpu = new HPCPU;
	VideoCard *videocard = new HPVideoCard;
	Memory *memory = new HPMemory;

	// 创建第一台电脑
	Computer *computer = new Computer(cpu, videocard, memory);
	// 电脑开始工作
	computer->Work();
	delete computer;
	// outputs:HP 的 CPU 开始工作了!
	//		   HP 的 显卡 开始工作了!
	//		   HP 的 内存条 开始工作了!
	//		   电脑零件:CPU、显卡和内存条占用的堆区内存已被释放!

	cout << "-----------------------------------------------" << endl;

	// 创建第二台电脑
	Computer *computer2 = new Computer(new IntelCPU, new IntelVideoCard, new IntelMemory);
	// 电脑开始工作
	computer2->Work();
	delete computer2;
	// outputs:Intel 的 CPU 开始工作了!
	//		   Intel 的 显卡 开始工作了!
	//		   Intel 的 内存条 开始工作了!
	//		   电脑零件:CPU、显卡和内存条占用的堆区内存已被释放!
}

int main() {
	
	func();

	system("pause");
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是我来晚了!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值