目录
一、多态的基本概念
1、静态多态
2、动态多态
二、多态的原理剖析
三、多态案例 —— 计算器类
四、纯虚函数和抽象类
五、虚析构和纯虚析构
1、虚析构
2、纯虚析构
六、多态案例 —— 组装电脑
多态是C++面向对象三大特性之一
-
两类多态
- 静态多态:函数重载和运算符重载(复用函数名)
- 动态多态:派生类和虚函数实现运行时多态
-
静态和动态的多态区别:
- 静态多态的函数地址早绑定,编译阶段确定函数地址
- 动态多态的函数地址晚绑定,运行阶段确定函数地址
测试代码:
#include
using namespace std;
class Person1
{
public:
void print(){
cout << "Person1" << endl;
}
};
class Person2 :public Person1
{
public:
void print(){
cout << "Person2" << endl;
}
};
void myPrint(Person1& p1){
p1.print();
}
int main(){
Person2 p2;
myPrint(p2); // 父类引用接受子类对象
system("pause");
return 0;
}
运行结果:
地址早绑定,编译阶段就确定了地址,无论传入的是子类的什么引用,调用的都是父类的函数。
-
想要调用子类的函数就要地址晚绑定(动态多态)
在父类的函数前加关键字 virtual 让函数成为虚函数
测试代码:
#include
using namespace std;
class Person1 {
public:
// 虚函数
virtual void print() {
cout << "Person1" << endl;
}
};
class Person2 :public Person1 {
public:
void print() {
cout << "Person2" << endl;
}
};
void myPrint(Person1& p1) {
p1.print();
}
int main() {
Person2 p2;
myPrint(p2);
system("pause");
return 0;
}
运行结果:
根据传入对象执行类的函数,print() 函数不能提早确定。
-
动态多态满足条件
1、有继承关系
2、子类重写父类虚函数
(重写需要函数完全一致,参数列表,返回值,函数名,子类 virtual 可以写也可以不写)
计算类中含有虚函数所占的空间大小
64bit 指针大小 8 字节
vs 开发者命令提示符工具,当子类继承父类虚函数
描述:
利用多态技术,设计实现两个操作数进行运算的计算器类-
多态的优点
- 代码组织结构清晰
- 可读性强
- 利于前期和后期的拓展以及维护
-
实际开发中提倡 开闭原则
开闭原则:对拓展进行开放,对修改进行关闭
测试代码:
#include
using namespace std;
// 实现计算器抽象类
class AbstractCalculator {
public:
virtual int getResult(){
return 0;
}
int m_Num1;
int m_Num2;
};
// 乘法类
class MulCalculator :public AbstractCalculator{
public:
virtual int getResult(){
return m_Num1 * m_Num2;
}
};
// 加法类
class AddCalculator :public AbstractCalculator{
public:
virtual int getResult(){
return m_Num1 + m_Num2;
}
};
//
// 可继续添加
void test(){
AbstractCalculator* abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
在多态中,通常父类中虚函数的实现是无意义的,主要都是调用子类重写的内容
-
因此可以将虚函数改为纯虚函数
-
纯虚函数语法:virtual 返回值类型 函数名(参数列表)= 0;
-
当类中有纯虚函数(父类),这个类称为 抽象类
-
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象函数
测试代码:
#include
using namespace std;
// 抽象类
class Base{
public:
// 纯虚函数
virtual void func() = 0;
};
class Son1 :public Base{
public:
// 重写父类虚函数
virtual void func()
{
cout << "Son1::func 函数调用" << endl;
}
};
class Son2 :public Base
{
public:
// 重写父类虚函数
virtual void func()
{
cout << "Son2::func 函数调用" << endl;
}
};
void test()
{
// 通过父类指针调用不同子类的函数
Base* base1 = new Son1;
base1->func();
Base* base2 = new Son2;
base2->func();
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
-
解决方法:将父类中的析构函数改为虚析构或者纯虚析构
-
虚析构和纯虚析构共性:
- 解决父类指针释放子类对象
- 都要有具体函数实现
虚析构和纯虚析构区别:
- 类中有纯虚析构,该类属于抽象类,无法实例化
-
虚析构语法:virtual ~类名() { } ;
纯虚析构语法:virtual ~类名() { } = 0;
问题:父类指针在析构时候不会调用子类中的析构函数,导致子类如果有堆区属性,就会出现内存泄漏
测试代码:
#include
#include
using namespace std;
class Person1 {
public:
// 构造
Person1() {
cout << "父类 构造函数调用" << endl;
}
// 纯虚函数
virtual void Print() = 0;
// 析构
~Person1() {
cout << "父类 析构函数调用" << endl;
}
};
class Person2 :public Person1 {
public:
// 构造
Person2(string name) {
cout << "子类 构造函数调用" << endl;
m_Name = new string(name);
}
// 重写
virtual void Print() {
cout << *m_Name << " 子类 中 Print" << endl;
}
// 析构函数释放堆区数据
~Person2() {
if (m_Name != NULL) {
cout << "子类 析构函数调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string* m_Name;
};
void test() {
// 父类指针指向子类对象,开辟堆区数据
Person1* p1 = new Person2("lihua");
p1->Print();
// 释放堆区数据
delete p1;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
修改:将父类的析构变成虚析构
virtual ~Person1() = 0;
如果纯虚析构只有声明会产生一个无法解析外部命令的错误(链接阶段的错误)
需要代码实现
class Person1 {
public:
// 纯虚析构
virtual ~Person1() = 0;
};
// 纯虚析构带代码实现
Person1::~Person1() {
cout << "Person1 下的纯虚析构函数" << endl;
}
类中含有纯虚函数这个类变为抽象类,无法实例化
代码:
#include
using namespace std;
// cpu 抽象类
class CPU{
public:
// cpu计算纯虚函数
virtual void calculator() = 0;
};
// gpu抽象类
class GPU{
public:
// gpu显示纯虚函数
virtual void display() = 0;
};
// 内存抽象类
class MEM{
public:
// memory存储纯虚函数
virtual void sterage() = 0;
};
// 继承 CPU 的子类,重写父类纯虚函数
class Intel_CPU :public CPU{
public:
// 重写
virtual void calculator(){
cout << "Inter_CPU" << endl;
}
};
// 继承 GPU 的子类,重写父类纯虚函数
class Intel_GPU :public GPU{
public:
// 重写
virtual void display(){
cout << "Inter_GPU" << endl;
}
};
// 继承 MEM 的子类,重写父类纯虚函数
class Intel_MEM :public MEM{
public:
// 重写
virtual void sterage(){
cout << "Inter_MEM" << endl;
}
};
class AMD_CPU :public CPU{
public:
// 重写
virtual void calculator(){
cout << "AMD_CPU" << endl;
}
};
class AMD_GPU :public GPU{
public:
// 重写
virtual void dispaly(){
cout << "AMD_GPU" << endl;
}
};
class AMD_MEM :public MEM
{
public:
// 重写
virtual void sterage(){
cout << "AMD_MEM" << endl;
}
};
// computer类
class Computer
{
public:
// 构造
Computer(CPU* cpu, GPU* gpu, MEM* mem){
m_cpu = cpu;
m_gpu = gpu;
m_mem = mem;
}
// 拼装函数
void work(){
m_cpu->calculator();
m_gpu->display();
m_mem->sterage();
}
// 析构
~Computer(){
if (m_cpu != NULL) {
delete m_cpu;
m_cpu = NULL;
}
if (m_gpu != NULL) {
delete m_gpu;
m_gpu = NULL;
}
if (m_mem != NULL) {
delete m_mem;
m_mem = NULL;
}
}
private:
CPU* m_cpu;
GPU* m_gpu;
MEM* m_mem;
};
void test(){
// 堆区创建配件
CPU* IntelCPU = new Intel_CPU;
GPU* IntelGPU = new Intel_GPU;
MEM* AMDMEM = new AMD_MEM;
// 实例化 Computer类 对象,传入指针
Computer* computer = new Computer(IntelCPU, IntelGPU, AMDMEM);
// 调用对象成员函数 - 拼装函数
computer->work();
// 销毁堆区数据
delete computer;
}
int main()
{
test();
system("pause");
return 0;
}