//
// main.cpp
// _day7
//
// Created by AchesonD16 贪玩巴斯 on 2021/5/7.
//
// 多态
一、多态的基本概念
// 多态的基本概念
//多态是C++ 面相对象三大特性之一
/*
多态分为两类
静态多态: 函数重载 和 运算符重载 属于 静态多态,复用函数名
动态多态: 派生类 和 虚函数 实现运行时 多态
静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
*/
//例子
/*
#include<iostream>
using namespace std;
class Animal
{
public:
//Speak函数的声明前加上 virtual 关键字 就为虚函数
//函数前面加上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& animal)
{
animal.speak();
}
二、多态成立的条件和使用方法
// 多态成立满足的条件:
// 1、有继承关系
// 2、子类 重写 父类中的虚函数
// 多态的使用方法:
// 父类的 指针 或者 引用 指向 子类对象
void test1()
{
Cat c;
Dospeak(c); // 子类父类之间可以直接互相调用
Dog d;
Dospeak(d);
}
int main()
{
test1();
system("pause");
return 0;
}
结果:
小猫类在说话
小狗类在说话
总结:
多态满足的条件:
子类、父类有继承的关系。
子类要 重写 父类中的虚函数
多态使用的条件:
必须要用
父类 指针 或 引用 必须 指向 子类对象
重写:
函数返回值类型 函数名 形参列表 必须完全一致,才叫做重写
*/
三、多态案例一-计算器类
// 多态案例一 - 计算器类
//案例描述:
//分别利用普通写法 和 多态技术, 设计实现两个操作数 进行运算的 计算器类
//多态的优点:
//1.代码组织结构清晰
//2.可读性强
//3.利于前期和后期的扩展以及维护
//示例:
/*
#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;
}
// 如果要提供新的运算,就需要修改最初的源码
// 我的编译器这里会报错
} // non-void function does not return a value in all control 这句话的意思是函数并不是所有分支都有返回值。
public:
int m_Num1;
int m_Num2;
};
void test1()
{
// 普通实现测试
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
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()
{
return 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()
{
//创建加法计算器
AbstractCalculator* abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc; // new:堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete。 用完了要销毁
//创建减法计算器
abc = new SubCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
//创建乘法计算器
abc = new MulCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main()
{
//test1();
test2();
system("pause");
return 0;
}
结果:
10 + 10 = 20
10 - 10 = 0
10 * 10 = 100
总结:
C++ 开发提倡利用多态设计程序架构,因为多态优点很多
*/
四、纯虚函数和抽象类
/*
一、纯虚函数和抽象类:
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将 虚函数 改为 纯虚函数
二、纯虚函数语法:
virtual 返回值类型 函数名(形参列表)= 0;
当类中有了纯虚函数,这个类也被称为 抽象类
三、抽象类的特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
*/
/*
#include<iostream>
using namespace std;
class Base
{
public:
//纯虚函数
//类中只要有一个纯虚函数就称为抽象类
//抽象类无法实例化对象
//子类必须重写父类中的纯虚函数,否则也属于抽象类
virtual void func() = 0; //语法
};
class Son : public Base
{
public:
virtual void func()
{
cout << "func调用" << endl;
}
};
void test1()
{
Base* b = NULL;
//b = new Base; //错误,抽象类无法实例化对象
b = new Son;
b->func();
delete b; //记得销毁
}
int main()
{
test1();
system("pause");
return 0;
}
结果:
func调用
*/
五、多态案例二
/*
多态案例二 - 制作饮品
案例描述:
制作饮品的大致流程为: 煮水 - 冲泡 - 倒入杯中 - 加入辅料
利用多态技术实现本案例,提供 抽象制作饮品基类,提供子类制作咖啡和茶叶
#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: //在子类重写父类的纯虚函数
//烧水
virtual void Boil()
{
cout << "煮农夫山泉!" << endl;
}
//冲泡
virtual void Brew()
{
cout << "冲泡咖啡!" << endl;
}
//倒入杯中
virtual void PourInCup()
{
cout << "将咖啡倒入杯中!" << endl;
}
//加入辅料
virtual void PutSomething()
{
cout << "加入牛奶!" << endl;
}
};
//制作茶水
class Tea : public AbstractDrinking
{
public:
//烧水
virtual void Boil()
{
cout << "煮自来水!" << endl;
}
//冲泡
virtual void Brew()
{
cout << "冲泡茶叶!" << endl;
}
//倒入杯中
virtual void PourInCup()
{
cout << "将茶水倒入杯中!" << endl;
}
virtual void PutSomething()
{
cout << "加入枸杞!" << endl;
}
};
//业务函数
void DoWork(AbstractDrinking* drink)
{
drink->MakeDrink();
delete drink;
}
void test1()
{
DoWork(new Coffee);
cout << "---------------" << endl;
DoWork(new Tea);
}
int main()
{
test1();
system("pause");
return 0;
}
结果:
煮农夫山泉!
冲泡咖啡!
将咖啡倒入杯中!
加入牛奶!
---------------
煮自来水!
冲泡茶叶!
将茶水倒入杯中!
加入枸杞!
*/
六、虚析构和纯虚虚构
// 虚析构和纯虚析构
/*
一、定义
多态使用时,如果子类中有属性开辟到 堆区 ,那么父类指针在释放时无法调用到子类的析构代码
解决方法:将父类中的析构函数 改为 虚析构 或者 纯虚析构
二、虚析构和纯虚析构的共性:
可以解决父类指针释放子类对象
都需要具体的函数实现
三、虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
四、
虚析构语法:
virtual ~类名()
{}
五、
纯虚析构语法:
virtual ~类名() = 0;
在类外定义
类名::~类名()
{}
#include<iostream>
using namespace std;
#include<string>
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数!" << endl;
}
virtual void Speak() = 0;
// 虚析构的定义方法
// //析构函数加上virtual关键字,变成 虚析构函数
// virtual ~Animal()
// {
// cout << "Animal 虚析构函数调用!" << endl;
// }
// 纯虚析构的定义方法
virtual ~Animal() = 0;
};
// 纯虚析构类外定义
Animal :: ~Animal()
{
cout << "Animal 纯虚析构的函数调用!" << endl;
}
//和包含 普通纯虚析构 的类一样,包含了 纯虚析构函数 的类也是一个抽象类。不能够被实例化。
class Cat : public Animal
{
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
~Cat()
{
cout << "Cat析构函数的调用!" << endl;
if( this->m_Name != NULL )
{
delete m_Name;
m_Name = NULL;
}
}
public:
string *m_Name;
};
void test1()
{
Animal *animal = new Cat("Tom");
animal->Speak();
//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
//怎么解决?给基类增加一个 虚析构函数
//虚析构函数 就是用来解决通过父类指针释放 子类对象
delete animal;
}
int main()
{
test1();
system("pause");
return 0;
}
结果:
Animal的构造函数!
Cat构造函数调用!
Tom小猫在说话!
Cat析构函数的调用!
Animal 纯虚析构的函数调用!
总结:
1、虚析构 或 纯虚析构 就是用来解决 通过父类指针 释放 子类对象
2、如果子类中没有使用 堆区, 可以不写 虚析构 或 纯虚析构
3、拥有 纯虚析构函数 的类也 属于 抽象类
*/
七、多态案例三
//案例:多态案例三 - 电脑组装
/*
案例描述:
电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作
*/
/*
#include <iostream>
using namespace std;
#include <string>
//抽象CPU类
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)
{
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
//提供工作的函数
void work()
{
//让零件工作起来,调用接口
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
//提供析构函数 释放3个电脑零件
~Computer()
{
//释放CPU零件
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; //cpu的零件指针
VideoCard* m_vc; //显卡零件指针
Memory* m_mem; //内存条零件指针
};
//具体厂商
//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;
}
};
//Lenovo厂商
class LenovoCPU : public CPU
{
public:
virtual void calculate()
{
cout << "Lenovo的CPU开始计算了!" << endl;
}
};
class LenovoVideoCard : public VideoCard
{
public:
virtual void display()
{
cout << "Lenovo的显卡开始显示了!" << endl;
}
};
class LenovoMemory : public Memory
{
public:
virtual void storage()
{
cout << "Lenovo的内存条开始存储了!" << endl;
}
};
void test1()
{
//第一台电脑零件
CPU* intelcpu = new IntelCPU;
VideoCard* intelvid = new IntelVideoCard;
Memory* intelmem = new IntelMemory;
cout << "第一台电脑开始工作:" << endl;
//创建第一台电脑
Computer* com1 = new Computer(intelcpu, intelvid, intelmem);
com1->work();
delete com1;
cout << "----------------------------" << endl;
cout << "第二台电脑开始工作:" << endl;
//第二台电脑组装
Computer* com2 = new Computer(new LenovoCPU,new LenovoVideoCard, new LenovoMemory);
com2->work();
delete com2;
cout << "----------------------------" << endl;
cout << "第三台电脑开始工作:" << endl;
//第三台电脑组装
Computer* com3 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);
com3->work();
delete com3;
}
int main()
{
test1();
system("pause");
return 0;
}
结果:
第一台电脑开始工作:
Intel的CPU开始计算了!
Intel的显卡开始显示了!
Intel的内存条开始存储了!
----------------------------
第二台电脑开始工作:
Lenovo的CPU开始计算了!
Lenovo的显卡开始显示了!
Lenovo的内存条开始存储了!
----------------------------
第三台电脑开始工作:
Lenovo的CPU开始计算了!
Lenovo的显卡开始显示了!
Lenovo的内存条开始存储了!
*/