多态的基本语法
多态分为二类:静态多态和动态多态
》》》静态多态:函数重载和运算符重载属于静态多态
》》》动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:静态多态的函数地址早绑定(编译阶段确认函数地址)动态多态的函数地址晚绑定(运行阶段确认函数地址)
程序举例
#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;
}