- 多态的基本语法
- 多态案例1 - 计算器类
- 纯虚函数和抽象类
- 多态案例2 - 学科学习过程
- 虚析构和纯虚析构
- 多态案例3 - 电脑组装
多态分为二类:静态多态和动态多态
》》》静态多态:函数重载和运算符重载属于静态多态
》》》动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:静态多态的函数地址早绑定(编译阶段确认函数地址)动态多态的函数地址晚绑定(运行阶段确认函数地址)
程序举例
#includeusing 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; }
此处我们使用虚函数实现晚绑定
#includeusing 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 - 计算器类原始实现
#includeusing namespace std; #include 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; }
多态实现
#includeusing 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多态案例2 - 学科学习过程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; }
#include虚析构和纯虚析构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多态案例3 - 电脑组装using namespace std; #include // 被纯虚析构函数修饰 -- 抽象类 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; }
#includeusing 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; }



