4.7 多态
多态的基本概念
靜态多态:函數重載和運算符重載。
動态多态:派生類和虛函數實作運作時多态。
差別:
靜态多态函數位址綁定,編譯階段确定函數位址。
動态多态的函數位址晚綁定,運作階段确定函數位址。
class Animal
{
public:
void speak(){
cout<<"動物在說話"<<endl;
}
};
class Cat: public Animal{
public:
void speak(){
cout<<"小貓在說話"<<endl;
}
};
class Dog: public Animal{
public:
void speak(){
cout<<"小狗在說話"<<endl;
}
};
//早綁定,綁定到Animal的speak方法。
//傳入貓時,也是指向Animal的speak,輸出動物在說話。
void doSpeak(Animal &animal){
animal.speak();
}
void test(){
Cat cat;
doSpeak(cat);
Dog dot;
doSpeak(dog);
}
而我們想要的效果是傳入貓時,輸出貓在說話。
隻要在Animal的方法
void speak()
前加上virtual關鍵字,變為
virtual void speak()
,就改寫為了虛函數。
虛函數就會在運作時确定函數位址。
總結:
動态多态滿足條件:
- 子類繼承父類
-
子類重寫父類的虛函數
(注意,重寫不是重載,重寫的傳回值、函數名、參數清單都相同)
動态多态的使用
父類的指針(或引用)指向子類的對象。
多态的原理
有虛函數的類會有一個vfptr (虛函數表指針),指向虛函數表。
虛函數表記錄的是函數的入口位址。
子類繼承父類,重寫父類的虛函數後,改寫了自己的虛函數表。
4.7.2 多态案例1——電腦
class AbstractCalculator
{
public :
virtual int getResult(){
return 0;
}
int num1;
int num2;
};
class AddCalculator: public AbstractCalculator
{
public :
int getResult(){
return num1 + num2;
}
};
class SubCalculator: public AbstractCalculator
{
public :
int getResult(){
return num1 - num2;
}
};
void test(){
AbstarctCalculator * abc = new AddCalculator;
abc->num1 = 10;
abc->num2 = 10;
cout<<abc.getResult()<<endl;
abc = new SubCalculator;
abc->num1 = 10;
abc->num2 = 10;
cout<<abc.getResult()<<endl;
}
好處:開閉原則
開發擴充,關閉修改。
在開發新功能時,不需要修改原來的代碼。
4.7.3 純虛函數和抽象類
有些函數永遠不會被真正的執行,不需要函數體。我們可以寫成純虛函數。
純虛函數
文法:在虛函數後面加=0;
virtual 傳回值類型 函數名(參數清單) = 0;
有純虛函數的類是抽象類。
抽象類無法執行個體化對象。
抽象類子類必須重寫抽象類的純虛函數,否則也是抽象類。
4.7.4 多态案例2-制作飲料
制作飲料的大緻流程是:煮水-沖泡-倒入杯中-加入輔料
利用多态實作這個抽象過程,然後實作子類分别制作咖啡和茶葉。
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: AbstractDrinking{
void Boil(){
cout<<"煮水"<<endl;
}
void Brew(){
cout<<"沖泡咖啡"<<endl;
}
void PourInCup(){
cout<<"倒入杯中"<<endl;
}
void PutSomething(){
cout<<"加糖"<<endl;
}
};
class Tea: AbstractDrinking{
void Boil(){
cout<<"煮開水"<<endl;
}
void Brew(){
cout<<"沖泡茶葉"<<endl;
}
void PourInCup(){
cout<<"倒入杯中"<<endl;
}
void PutSomething(){
cout<<"加枸杞"<<endl;
}
};
void doWork(AbstractDrinking* abs){
abs->makeDrink();
delete abs;
}
void test01(){
doWork(new Coffee);
cout<<"-----------"<<endl;
doWork(new Tea);
}
4.7.5 虛析構和純虛析構
多态使用時,子類屬性開辟到堆區時,父類指針在釋放時(delete)不調用子類的析構函數。
Animal::~Animal() {
...
}
4.7.6 多态案例3-電腦組裝
#include<iostream>
#include<string>
using namespace std;
class CPU
{
public:
virtual void calculate() = 0;
};
class VideoCard
{
public:
virtual void display() = 0;
};
class Memory
{
public:
virtual void store() = 0;
};
class Computer
{
public:
Computer(CPU * t_cpu, VideoCard * t_vc, Memory * t_mem ) {
cpu = t_cpu;
vc = t_vc;
memory = t_mem;
}
~Computer() {
if (cpu != NULL) {
delete cpu;
cpu = NULL;
}
if (vc != NULL) {
delete vc;
vc = NULL;
}
if (memory != NULL) {
delete memory;
memory = NULL;
}
}
void work() {
cpu->calculate();
vc->display();
memory->store();
}
private:
CPU * cpu;
VideoCard * vc;
Memory * memory;
};
//具體廠商
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 store() {
cout << "Intel 的記憶體工作中..." << endl;
}
};
class NvidiaVideoCard : public VideoCard
{
public:
virtual void display() {
cout << "Nvidia 的顯示卡工作中..." << endl;
}
};
void test01()
{
Computer * cp1 = new Computer(new IntelCPU,
new IntelVideoCard,new IntelMemory);
cp1->work();
delete cp1;
cout << endl;
Computer * cp2 = new Computer(new IntelCPU,
new NvidiaVideoCard, new IntelMemory);
cp2->work();
delete cp2;
}
int main() {
test01();
return 0;
}