C++学习 16类与对象(五)多态

本文详细介绍了C++中多态的概念及其两种形式:静态多态和动态多态,并通过实例展示了如何利用动态多态实现代码的灵活性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类与对象

多态

多态是c++面向对象的三大特性之一

多态分为2类:

静态多态
函数重载和运算符重载属于静态多态,复用函数名
动态多态
派生类和虚函数实现运行时多态
二者之间的区别

  • 静态多态的地址早绑定-编译阶段确定函数地址
  • 动态多态的地址晚绑定-运行阶段确定函数地址

动态多态的满足条件:

  • 要有继承关系
  • 子类要重写父类的虚函数

动态多态的使用:
父类的引用指向子类对象
多态的优点:

  • 代码组织结构清晰
  • 可读性强
  • 利于前期和后期的扩展与维护

动态多态的简单实现和使用案列:

#include <iostream>
using namespace std;

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

class Cat : public Animal{
public:
	void speak() {
		cout << "喵喵喵.." << endl;
	}
};
class Dog : public Animal {
public:
	void speak() {
		cout << "汪汪汪.." << endl;
	}
};

//动态多态的使用: 父类的引用指向子类对象
void doSpeak(Animal& a) {
	a.speak();
}

void test01() {
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}
int main() {
	test01();
	system("pause");
	return 0;
}
动态多态的原理刨析
vfptr 虚函数指针
v -virtual
f -function
ptr - pointer

vftable 虚函数表
表内记录虚函数地址
当子类重写父类的虚函数,子类的虚函数表内部会替换成子类的虚函数地址
当父类的指针或引用指向子类的时候,就发生了多态
在这里插入图片描述
可以看到Cat的vftable中的方法是Cat的speak()地址,如果注释掉Cat类对父类Aniaml的虚函数speak()的重写,则Cat的vftable存放的是Animal的speak()地址,图示如下
在这里插入图片描述

多态案例1计算机类

#include<iostream>
using namespace std;

class AbstractCalculator {
public:
	int m_Num1;
	int m_Num2;
	virtual int getResult() {
		return 0;
	}
};
//加
class AddCalculator:public AbstractCalculator {
	int getResult() {
		return m_Num1 + m_Num2;
	}
};
//减
class ReduceCalculator :public AbstractCalculator {
	int getResult() {
		return m_Num1 - m_Num2;
	}
};
//乘
class RideCalculator :public AbstractCalculator {
	int getResult() {
		return m_Num1 * m_Num2;
	}
};
//除
class DivideCalculator :public AbstractCalculator {
	int getResult() {
		return m_Num1 / m_Num2;
	}
};

void test01() {
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 15;
	abc->m_Num2 = 15;
	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;

	//new出来的数据存放在堆区,delete后删除的是堆区的数据,而指针在栈区,未被消除,可以直接使用
	abc = new DivideCalculator;
	abc->m_Num1 = 15;
	abc->m_Num2 = 3;
	cout << abc->m_Num1 << "/" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//堆区的数据要在不使用的时候手动释放
	delete abc;
}
int main() {
	test01();
	system("pause");
	return 0;
}

纯虚函数和抽象类

在多态中父类的虚函数基本都是没有意义的,主要调用子类重写的内容
因此可以将虚函数改成纯虚函数
语法

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

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

抽象类特点

  • 无法示例化对象
  • 子类必须重写抽象类的纯虚函数,否则子类也算抽象类
    在这里插入图片描述

多态案例2制作饮品

#include <iostream>
using namespace std;
class AbstractDrinks {
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//加佐料
	virtual void Seasoning() = 0;
	//倒入杯中
	virtual void PourIntoCup() = 0;
	void makeDrinks() {

		Boil();
		Seasoning();
		Brew();
		PourIntoCup();
	}
};

class MakeCoffe:public AbstractDrinks {
public:
	void Boil() {
		cout << "煮水至70度" << endl;
	}
	void Seasoning() {
		cout << "加入碾磨好的咖啡豆和糖块" << endl;
	}
	void Brew() {
		cout << "冲泡3分钟" << endl;
	}
	void PourIntoCup() {
		cout << "倒入准备好的杯子中" << endl;
	}
};
class MakeTea :public AbstractDrinks {
public:
	void Boil() {
		cout << "煮水至100度" << endl;
	}
	void Seasoning() {
		cout << "加入大红袍" << endl;
	}
	void Brew() {
		cout << "冲洗一次,泡1-2分钟" << endl;
	}
	void PourIntoCup() {
		cout << "倒入准备好的杯子中" << endl;
	}
};

void doDrink(AbstractDrinks *abs) {
	abs->makeDrinks();
	delete abs;
}

void test01() {
	cout << "制作咖啡" << endl;
	doDrink(new MakeCoffe);
	cout << "泡茶" << endl;
	doDrink(new MakeTea);
}
int main() {
	
	test01();
	system("pause");
	return 0;
}

虚析构和纯虚析构

多态使用时,如果子类有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构函数
解决方式
将父类的析构函数改为虚析构纯虚析构函数

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

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

语法

虚析构语法

virtual ~类名(){}

纯虚析构语法

virtual ~类名() = 0;

注意事项:
纯虚析构也需要有代码的实现
在类外实现,通过作用域区分

类名::~类名(){

}

多态案例3组装计算机

#include<iostream>
using namespace std;

class CPU {
public:
	virtual void calculator() = 0;
};
class VideoCard {
public:
	virtual void display() = 0;
};
class Memory {
public:
	virtual void storage() = 0;
};
//Intel厂商
class IntelCPU :public CPU {
public:
	void calculator() {
		cout << "Intel的CPU开始工作了" << endl;
	}
};
class IntelVideoCard :public VideoCard {
public:
	void display() {
		cout << "Intel的VideoCard开始工作了" << endl;
	}
};
class IntelMemory :public Memory {
public:
	void storage() {
		cout << "Intel的Memory开始工作了" << endl;
	}
};
//Lenove
class LenoveCPU :public CPU {
public:
	void calculator() {
		cout << "Lenove的CPU开始工作了" << endl;
	}
};
class LenoveVideoCard :public VideoCard {
public:
	void display() {
		cout << "Lenove的VideoCard开始工作了" << endl;
	}
};
class LenoveMemory :public Memory {
public:
	void storage() {
		cout << "Lenove的Memory开始工作了" << endl;
	}
};
class Computer {
public:
	Computer(CPU* cpu, VideoCard* vc, Memory* mem) {
		m_Cpu = cpu;
		m_Vc = vc;
		m_Mem = mem;

	}
	//提供工作的函数
	void doWork() {
		m_Cpu->calculator();
		m_Vc->display();
		m_Mem->storage();
	}
	~Computer() {
		if (m_Cpu != NULL) {
			delete m_Cpu;
			m_Cpu = NULL;
		}
		if (m_Vc != NULL) {
			delete m_Vc;
			m_Vc = NULL;
		}
		if (m_Mem != NULL) {
			delete m_Mem;
			m_Mem = NULL;
		}
	}
private:
	CPU* m_Cpu;
	VideoCard* m_Vc;
	Memory* m_Mem;
};

void test01() {

	//组装第一台电脑
	cout << "组装第一台电脑" << endl;
	Computer* c1 = new Computer(new IntelCPU, new IntelVideoCard, new  LenoveMemory);
	c1->doWork();
	delete c1;
	cout << "___________________________" << endl;
	cout << "组装第二台电脑" << endl;
	Computer* c2 = new Computer(new IntelCPU, new IntelVideoCard, new IntelMemory);
	c2->doWork();
	delete c2;
	cout << "___________________________" << endl;
	cout << "组装第三台电脑" << endl;
	Computer* c3 = new Computer(new  LenoveCPU, new  LenoveVideoCard, new  LenoveMemory);
	c3->doWork();
	delete c3;


}
int main() {
	test01();
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值