C++核心编程--多态篇

4.7、多态

4.7.1、多态的基本概念

多态是C++面向对象三大特征之一

多态分为两类

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
  1. 动态多态满足条件
    • 有继承关系
    • 子类重写父类的虚函数
  2. 动态多态使用
    • 父类的指针或引用 指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

class Animal {
public:
	//虚函数
	virtual void  spreak() {
		cout << "动物在说话" << endl;	
	}
};

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

class Dog : public Animal {
public:
	void spreak() {
		cout << "狗在说话" << endl;
	}
};

//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
//如果想让猫说话,就不能提前绑定,云瑶在运行阶段绑定,及地址晚绑定 
void doSpreak(Animal &animal) {
	animal.spreak();
}

void test01() {
	Cat cat;
	doSpreak(cat);
	Dog dog;
	doSpreak(dog);
}
4.7.2、多态案例-计算器类
  • 多态的优点:
    • 代码组织结构清晰
    • 可读性强
    • 利于前期和后期扩展以及维护
//实现计算机的抽象类
class AbstractCalculator {
public:
	virtual int getResult() {
		return 0;
	}

	int m_Num1,m_Num2;
};

//减法计算机类
class SubCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 - m_Num2;
	}
};
//加法计算机类
class AddCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 + m_Num2;
	}
};

void test01() {
	
	AbstractCalculator *a1 = new AddCalculator;
	a1->m_Num1 = 100;
	a1->m_Num2 = 200;
	cout << a1->m_Num1 << " + " << a1->m_Num2 << " = " << a1->getResult() << endl;
	//回收a1
	delete a1;

	a1 = new SubCalculator();
	a1->m_Num1 = 200;
	a1->m_Num2 = 100;
	cout << a1->m_Num1 << " - " << a1->m_Num2 << " = " << a1->getResult() << endl;
	//回收a1
	delete a1;
}

image-20230930093236142

4.7.3、纯虚函数和抽象类

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

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

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

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//纯虚函数和抽象类
class Base {
public:
	virtual void func() = 0;
};

//1、纯虚函数无法实例对象
//void test01() {
//	Base b;
//	new Base;
//}

//2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//class Son : public Base {
//public:
//
//};
//
//void test02() {
//	Son s;
//}

//3、正确写法
class Son : public Base {
public:
	virtual void func() {
		cout << "func函数被调用" << endl;
	};
};

void test03() {
	Base* base = new Son;
	base->func();
}
4.7.4、多态案例二 - 制作饮品

案例描述:

制作饮品时大致的流程为: 煮水 - 冲泡 - 导入杯中 - 加入辅料

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡喝茶叶

image-20230930102207998

//饮品抽象类
class MakeDreak {
public:
	virtual void Boil() = 0;
	virtual void Rush() = 0;
	virtual void Fall() = 0;
	virtual void Add() = 0;

	void markDrink() {
		Boil();
		Rush();
		Fall();
		Add();
	};
};

class Coffee : public MakeDreak {
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	virtual void Rush() {
		cout << "冲泡咖啡" << endl;
	}
	virtual void Fall() {
		cout << "倒入杯中" << endl;
	}
	virtual void Add() {
		cout << "加糖和牛奶" << endl;
	}
};

class Tea : public MakeDreak {
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	virtual void Rush() {
		cout << "冲泡茶叶" << endl;
	}
	virtual void Fall() {
		cout << "倒入杯中" << endl;
	}
	virtual void Add() {
		cout << "柠檬" << endl;
	}
};

//制作函数
void doWork(MakeDreak* abs) {
	abs->markDrink();
}

void test01() {
	doWork(new Coffee);
	cout << "-----------------" << endl;
	doWork(new Tea);
}
4.7.5、虚析构和纯虚析构

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

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

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

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

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0

类名::~类名(){}

class Animal {
public:
	Animal() {
		cout << "Animal构造函数被调用" << endl;
	}
	//利用虚析构可以解决 父类指针释放子类对象
	virtual ~Animal()
	{
		cout << "Animal析构函数被调用" << endl;
	}

	//纯虚函数
	virtual void spreak()  = 0;

};

class Cat : public Animal {
public:
	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		m_Name =  new string(name);
	}
	virtual void spreak() {
		cout << *m_Name<<"猫正在说话" << endl;
	}
	~Cat()
	{
		if (m_Name !=NULL)
		{
			cout << "Cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string *m_Name;
};

void test01() {
	Animal* animal = new Cat("Tom");
	animal->spreak();
	//父类指针在析构时候,不会调用子类中析构函数,
	//导致子类如果有堆区属性,出现内存泄露
	delete animal;
}
4.7.6、多态案例三 - 电脑组装

案例描述:

电脑主要组成部件为CPU (用于计算),显卡(用于显示),内存条(用于存储)将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Inter厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

//cpu类
class CPU {
public:
	//抽象的计算机函数
	virtual void caculate() = 0;
};

//显卡类
class Graphics {
public:
	//抽象的显卡
	virtual void show() = 0;
};

//内存类
class RAM {
public:
	virtual void save() = 0;
};


class Computer {
public:
	Computer(CPU* cpu, Graphics* gpu, RAM* ram) {
		m_cpu = cpu;
		m_gpu = gpu;
		m_ram = ram;
	}

	void work() {
		m_cpu->caculate();
		m_gpu->show();
		m_ram->save();
	}
	//提供析构函数 释放3个电脑零件
	~Computer()
	{
		//释放cpu
		if (m_cpu!=NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		//释放显卡
		if (m_gpu !=NULL)
		{
			delete m_gpu;
			m_gpu = NULL;
		}
		//释放内存条
		if (m_ram !=NULL)
		{
			delete m_ram;
			m_ram = NULL;
		}
	}
private:
	CPU* m_cpu; //CPU的零件指针
	Graphics* m_gpu;
	RAM* m_ram;
};

//Intel厂商
class IntelCPU : public CPU {
public:
	virtual void caculate() {
		cout << "Intel的CPU开始计算了" << endl;
	}
};
class IntelGPU : public Graphics {
public:
	virtual void show() {
		cout << "Intel的GPU开始显示了" << endl;
	}
};
class IntelRAM : public RAM {
public:
	virtual void save() {
		cout << "Intel的RAM开始存储了" << endl;
	}
};

//Lenovo厂商
class LenovoCPU : public CPU {
public:
	virtual void caculate() {
		cout << "AMD的CPU开始计算了" << endl;
	}
};
class LenovoGPU : public Graphics {
public:
	virtual void show() {
		cout << "AMD的GPU开始显示了" << endl;
	}
};
class LenovoRAM : public RAM {
public:
	virtual void save() {
		cout << "AMD的RAM开始存储了" << endl;
	}
};


void test01() {
	//第一台电脑零件
	CPU * intelCpu = new IntelCPU;
	Graphics * IntelGpu = new IntelGPU;
	RAM * IntelRam = new IntelRAM;

	//创建第一台电脑
	cout << "第一台电脑开始工作" << endl;
	Computer * computer1 = new Computer(intelCpu, IntelGpu, IntelRam);
	computer1->work();
	delete computer1;

	//第二台电脑
	cout << "---------------------" << endl;
	cout << "第二台电脑开始工作" << endl;
	Computer* computer2 = new Computer(new LenovoCPU, new LenovoGPU, new LenovoRAM);
	computer2->work();
	delete computer2;

	//第三台电脑
	cout << "---------------------" << endl;
	cout << "第三台电脑开始工作" << endl;
	Computer* computer3 = new Computer(new LenovoCPU, new IntelGPU, new LenovoRAM);
	computer3->work();
	delete computer3;


}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

周粥粥ya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值