目錄
10.?多態
10.1 多態的基本概念
10.2 多態案例一-計算器類
10.3 純虛函數和抽象類
10.4 多態案例二-制作飲品
10.5 虛析構和純虛析構
10.6 多態案例三-電腦組裝
10.?多態
10.1 多態的基本概念
多態是C++面向對象三大特性之一
多態分為兩類
靜態多態: 函數重載 和 運算符重載屬于靜態多態,復用函數名
動態多態: 派生類和虛函數實現運行時多態
靜態多態和動態多態區別:
靜態多態的函數地址早綁定 - 編譯階段確定函數地址
動態多態的函數地址晚綁定 - 運行階段確定函數地址
示例:
class Animal
{
public://Speak函數就是虛函數//函數前面加上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 test01()
{Cat cat;DoSpeak(cat);
?
?Dog dog;DoSpeak(dog);
}
?
?
int main() {
?test01();
?system("pause");
?return 0;
}
總結:
多態滿足條件
有繼承關系
子類重寫父類中的虛函數
多態使用條件
父類指針或引用指向子類對象
重寫:函數返回值類型 函數名 參數列表 完全一致稱為重寫
10.2 多態案例一-計算器類
案例描述:
分別利用普通寫法和多態技術,設計實現兩個操作數進行運算的計算器類
多態的優點:
代碼組織結構清晰
可讀性強
利于前期和后期的擴展以及維護
示例:
//普通實現
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;}//如果要提供新的運算,需要修改源碼}
public:int m_Num1;int m_Num2;
};
?
void test01()
{//普通實現測試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 test02()
{//創建加法計算器AbstractCalculator *abc = new AddCalculator;abc->m_Num1 = 10;abc->m_Num2 = 10;cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;delete abc; ?//用完了記得銷毀
?//創建減法計算器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() {
?//test01();
?test02();
?system("pause");
?return 0;
}
總結:C++開發提倡利用多態設計程序架構,因為多態優點很多
10.3 純虛函數和抽象類
在多態中,通常父類中虛函數的實現是毫無意義的,主要都是調用子類重寫的內容
因此可以將虛函數改為純虛函數
純虛函數語法:
virtual 返回值類型 函數名 (參數列表)= 0 ;
當類中有了純虛函數,這個類也稱為抽象類
抽象類特點:
無法實例化對象
子類必須重寫抽象類中的純虛函數,否則也屬于抽象類
示例:
class Base
{
public://純虛函數//類中只要有一個純虛函數就稱為抽象類//抽象類無法實例化對象//子類必須重寫父類中的純虛函數,否則也屬于抽象類virtual void func() = 0;
};
?
class Son :public Base
{
public:virtual void func() {cout << "func調用" << endl;};
};
?
void test01()
{Base * base = NULL;//base = new Base; // 錯誤,抽象類無法實例化對象base = new Son;base->func();delete base;//記得銷毀
}
?
int main() {
?test01();
?system("pause");
?return 0;
}
10.4 多態案例二-制作飲品
案例描述:
制作飲品的大致流程為:煮水 - 沖泡 - 倒入杯中 - 加入輔料
利用多態技術實現本案例,提供抽象制作飲品基類,提供子類制作咖啡和茶葉
示例:
//抽象制作飲品
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 test01() {DoWork(new Coffee);cout << "--------------" << endl;DoWork(new Tea);
}
?
?
int main() {
?test01();
?system("pause");
?return 0;
}
10.5 虛析構和純虛析構
多態使用時,如果子類中有屬性開辟到堆區,那么父類指針在釋放時無法調用到子類的析構代碼
解決方式:將父類中的析構函數改為虛析構或者純虛析構
虛析構和純虛析構共性:
可以解決父類指針釋放子類對象
都需要有具體的函數實現
虛析構和純虛析構區別:
如果是純虛析構,該類屬于抽象類,無法實例化對象
虛析構語法:
virtual ~類名(){}
純虛析構語法:
virtual ~類名() = 0;
類名::~類名(){}
示例:
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 test01()
{Animal *animal = new Cat("Tom");animal->Speak();
?//通過父類指針去釋放,會導致子類對象可能清理不干凈,造成內存泄漏//怎么解決?給基類增加一個虛析構函數//虛析構函數就是用來解決通過父類指針釋放子類對象delete animal;
}
?
int main() {
?test01();
?system("pause");
?return 0;
}
總結:
1. 虛析構或純虛析構就是用來解決通過父類指針釋放子類對象
2. 如果子類中沒有堆區數據,可以不寫為虛析構或純虛析構
3. 擁有純虛析構函數的類也屬于抽象類
10.6 多態案例三-電腦組裝
案例描述:
電腦主要組成部件為 CPU(用于計算),顯卡(用于顯示),內存條(用于存儲)
將每個零件封裝出抽象基類,并且提供不同的廠商生產不同的零件,例如Intel廠商和Lenovo廠商
創建電腦類提供讓電腦工作的函數,并且調用每個零件工作的接口
測試時組裝三臺不同的電腦進行工作
示例:
#include<iostream>
using namespace std;
?
//抽象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 test01()
{//第一臺電腦零件CPU * intelCpu = new IntelCPU;VideoCard * intelCard = new IntelVideoCard;Memory * intelMem = new IntelMemory;
?cout << "第一臺電腦開始工作:" << endl;//創建第一臺電腦Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);computer1->work();delete computer1;
?cout << "-----------------------" << endl;cout << "第二臺電腦開始工作:" << endl;//第二臺電腦組裝Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);;computer2->work();delete computer2;
?cout << "-----------------------" << endl;cout << "第三臺電腦開始工作:" << endl;//第三臺電腦組裝Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);;computer3->work();delete computer3;
?
}