C++ 類和物件--多型
阿新 • • 發佈:2022-01-12
4.7 多型
4.7.1 多型的基本概念
多型是C++面向物件三大特性之一
多型分為兩類
- 靜態多型: 函式過載 和 運算子過載屬於靜態多型,複用函式名
- 動態多型: 派生類和虛擬函式實現執行時多型
靜態多型和動態多型區別:
- 靜態多型的函式地址早繫結 - 編譯階段確定函式地址
- 動態多型的函式地址晚繫結 - 執行階段確定函式地址
下面通過案例進行講解多型
#include <iostream> using namespace std; // 多型 // 動物類 class Animal { public: // 虛擬函式 virtual void speak() { cout << "動物說話" << endl; } }; // 貓類 class Cat : public Animal { public: // 重寫 函式返回值型別 函式名 函式列表 完全相同 virtual void speak() { cout << "小貓在說話" << endl; } }; // 狗類 class Dog : public Animal { public: void speak() { cout << "小狗在說話" << endl; } }; // 執行說話的函式 // 地址早繫結 在編譯階段確定函式地址 // 如果想執行讓貓說話,那麼這個函式地址就不能提前繫結,需要在執行階段繫結,地址晚繫結 // 動態多型滿足條件 // 1、有繼承關係 // 2、子類重寫父類的虛擬函式 // 動態多型使用 // 父類的指標或者引用 指向子類物件 void doSpeak(Animal* animal) // Animal &animal = cat; { animal->speak(); } void test01() { Cat cat; doSpeak(&cat); Dog dog; doSpeak(&dog); } int main() { test01(); system("pause"); return 0; }
總結:
多型滿足條件
- 有繼承關係
- 子類重寫父類中的虛擬函式
多型使用條件
- 父類指標或引用指向子類物件
重寫:函式返回值型別 函式名 引數列表 完全一致稱為重寫
4.7.2 多型案例一-計算器類
案例描述:
分別利用普通寫法和多型技術,設計實現兩個運算元進行運算的計算器類
多型的優點:
- 程式碼組織結構清晰
- 可讀性強
- 利於前期和後期的擴充套件以及維護
示例:
#include <iostream> using namespace std; // 分別利用普通寫法和多型技術實現計算器 // 普通寫法 class Calculator { public: int getResult(string oper) { if (oper == "+") { return m_Numl + m_Num2; } else if (oper == "-") { return m_Numl - m_Num2; } else if (oper == "*") { return m_Numl * m_Num2; } else { return m_Numl / m_Num2; } // 如果想擴充套件新的功能,需求改原始碼 // 在真實開發中 提倡 開閉原則 // 開閉原則:對擴充套件進行開放,對修改進行關閉 } int m_Numl; // 運算元1 int m_Num2; // 運算元2 }; void test01() { // 建立計算器物件 Calculator c; c.m_Numl = 10; c.m_Num2 = 10; cout << c.m_Numl << " + " << c.m_Num2 << " = " << c.getResult("+") << endl; cout << c.m_Numl << " - " << c.m_Num2 << " = " << c.getResult("-") << endl; cout << c.m_Numl << " * " << c.m_Num2 << " = " << c.getResult("*") << endl; cout << c.m_Numl << " / " << c.m_Num2 << " = " << c.getResult("/") << endl; } // 利用多型實現計算器 // 實現計算器抽象類 // 多型好處: // 1、組織結構清晰 // 2、可讀性強 // 3、對於前期和後期擴充套件以及維護性高 // 實現計算器抽象類 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 = 20; cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; // 用完後記得銷燬 delete abc; // 減法運算 abc = new SubCalculator; abc->m_Num1 = 100; abc->m_Num2 = 20; cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; // 用完後記得銷燬 delete abc; // 乘法法運算 abc = new MulCalculator; abc->m_Num1 = 100; abc->m_Num2 = 20; cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; // 用完後記得銷燬 delete abc; } int main() { // test01(); test02(); system("pause"); return 0; }
總結:C++開發提倡利用多型設計程式架構,因為多型優點很多
4.7.3 純虛擬函式和抽象類
在多型中,通常父類中虛擬函式的實現是毫無意義的,主要都是呼叫子類重寫的內容
因此可以將虛擬函式改為純虛擬函式
純虛擬函式語法:virtual 返回值型別 函式名 (引數列表)= 0 ;
當類中有了純虛擬函式,這個類也稱為抽象類
抽象類特點:
- 無法例項化物件
- 子類必須重寫抽象類中的純虛擬函式,否則也屬於抽象類
示例:
#include <iostream> using namespace std; // 純虛擬函式和抽象類 class Base { public: // 純虛擬函式 // 只要有一個純虛擬函式,這個類稱為抽象類 // 抽象類特點: // 1、無法例項化物件 // 2、抽象類的子類 必須要重新父類中純虛擬函式,否則也屬於抽象類 virtual void func() = 0; }; class Son : public Base { public: virtual void func() { cout << "func函式呼叫" << endl; } }; void test01() { // Base b; // 抽象類無法例項化物件 // new Base; // 抽象類無法例項化物件 Son s; // 子類必須重寫父類中的純虛擬函式,否則無法例項化物件 Base* base = new Son; base->func(); // 記得銷燬 delete base; } int main() { test01(); system("pause"); return 0; }
4.7.4 多型案例二-製作飲品
案例描述:
製作飲品的大致流程為:煮水 - 沖泡 - 倒入杯中 - 加入輔料
利用多型技術實現本案例,提供抽象製作飲品基類,提供子類製作咖啡和茶葉
示例:
#include <iostream>
using namespace std;
// 多型案例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 : public AbstractDrinking
{
// 煮水
virtual void Boil()
{
cout << "煮農夫山泉" << endl;
}
// 沖泡
virtual void Brew()
{
cout << "沖泡咖啡" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
// 加入輔料
virtual void PutSomething()
{
cout << "加入糖和牛奶" << endl;
}
};
// 製作茶葉
class Tea : public AbstractDrinking
{
// 煮水
virtual void Boil()
{
cout << "煮礦泉水" << endl;
}
// 沖泡
virtual void Brew()
{
cout << "沖泡茶葉" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
// 加入輔料
virtual void PutSomething()
{
cout << "加入枸杞" << endl;
}
};
void doWork(AbstractDrinking* abs)
{
abs->makeDrink();
delete abs; // 釋放
}
void test01()
{
// 製作咖啡
doWork(new Coffee);
cout << "-----------------------" << endl;
// 製作茶葉
doWork(new Tea);
}
int main()
{
test01();
system("pause");
return 0;
}
4.7.5 虛析構和純虛析構
多型使用時,如果子類中有屬性開闢到堆區,那麼父類指標在釋放時無法呼叫到子類的析構程式碼
解決方式:將父類中的解構函式改為虛析構或者純虛析構
虛析構和純虛析構共性:
- 可以解決父類指標釋放子類物件
- 都需要有具體的函式實現
虛析構和純虛析構區別:
- 如果是純虛析構,該類屬於抽象類,無法例項化物件
虛析構語法:
virtual ~類名(){}
純虛析構語法:
virtual ~類名() = 0;
類名::~類名(){}
示例:
#include <iostream>
using namespace std;
// 虛析構和純虛析構
class Animal
{
public:
Animal()
{
cout << "Animal 建構函式呼叫" << endl;
}
// 利用虛析構可以解決 父類指標釋放子類物件時不乾淨的問題
//virtual ~Animal()
//{
// cout << "Animal 虛解構函式呼叫" << endl;
//}
// 純虛析構 需要宣告也需要實現
// 有了純虛析構之後,這個類也屬於抽象類,無法例項化物件
virtual ~Animal() = 0;
// 純虛擬函式
virtual void speak() = 0;
};
Animal::~Animal()
{
cout << "Animal 純虛解構函式呼叫" << endl;
}
class Cat : public Animal
{
public:
Cat(string name)
{
cout << "Cat 建構函式呼叫" << endl;
m_Name = new string(name);
}
~Cat()
{
if (m_Name != NULL)
{
cout << "Cat 解構函式呼叫" << endl;
delete m_Name;
m_Name = NULL;
}
}
virtual void speak()
{
cout << *m_Name << "小貓在說話" << endl;
}
string* m_Name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->speak();
// 父類指標在析構時候 不會呼叫子類中解構函式,導致子類如果有堆區屬性,出現記憶體洩漏
delete animal;
}
int main()
{
test01();
system("pause");
return 0;
}
總結:
1. 虛析構或純虛析構就是用來解決通過父類指標釋放子類物件
2. 如果子類中沒有堆區資料,可以不寫為虛析構或純虛析構
3. 擁有純虛解構函式的類也屬於抽象類
4.7.6 多型案例三-電腦組裝
案例描述:
電腦主要組成部件為 CPU(用於計算),顯示卡(用於顯示),記憶體條(用於儲存)
將每個零件封裝出抽象基類,並且提供不同的廠商生產不同的零件,例如Intel廠商和Lenovo廠商
建立電腦類提供讓電腦工作的函式,並且呼叫每個零件工作的介面
測試時組裝三臺不同的電腦進行工作
示例:
#include <iostream>
using namespace std;
// 抽象 CPU 類
class CPU
{
public:
// 抽象的計算函式
virtual void calculate() = 0;
};
// 抽象顯示卡類
class VideCard
{
public:
// 抽象的顯示函式
virtual void display() = 0;
};
// 抽象記憶體條類
class Memory
{
public:
// 抽象的儲存函式
virtual void storage() = 0;
};
// 電腦類
class Conputer
{
public:
Conputer(CPU* cpu, VideCard* vc, Memory* men)
{
this->cpu = cpu;
this->vc = vc;
this->men = men;
}
// 提供工作函式
void work()
{
// 讓零件工作起來,呼叫介面
cpu->calculate();
vc->display();
men->storage();
}
// 提供解構函式 釋放 3 個電腦零件
~Conputer()
{
// 釋放 cpu
if (cpu != NULL)
{
delete cpu;
cpu = NULL;
}
// 實現顯示卡
if (vc != NULL)
{
delete vc;
vc = NULL;
}
// 實現記憶體條
if (men != NULL)
{
delete men;
men = NULL;
}
}
private:
CPU* cpu; // CPU的零件指標
VideCard* vc; // 顯示卡零件指標
Memory* men; // 記憶體條零件指標
};
// Inter 廠商
class InterCpu : public CPU
{
public:
virtual void calculate()
{
cout << "Inter 的 Cpu開始計算了!" << endl;
}
};
class InterVideCard : public VideCard
{
public:
virtual void display()
{
cout << "Inter 的顯示卡開始顯示了!" << endl;
}
};
class InterMemory : public Memory
{
public:
virtual void storage()
{
cout << "Inter 的記憶體條開始儲存了!" << endl;
}
};
// Lenovo 廠商
class LenovoCpu : public CPU
{
public:
virtual void calculate()
{
cout << "Lenovo 的 Cpu開始計算了!" << endl;
}
};
class LenovoVideCard : public VideCard
{
public:
virtual void display()
{
cout << "Lenovo 的顯示卡開始顯示了!" << endl;
}
};
class LenovoMemory : public Memory
{
public:
virtual void storage()
{
cout << "Lenovo 的記憶體條開始儲存了!" << endl;
}
};
void test01()
{
// 第一臺的電腦零件
cout << "第一臺電腦開始工作" << endl;
CPU* interCpu = new InterCpu;
VideCard* interVc = new InterVideCard;
Memory* interMen = new InterMemory;
Conputer* compute1 = new Conputer(interCpu, interVc, interMen);
compute1->work();
delete compute1;
cout << "-----------------------------------" << endl;
cout << "第二臺電腦開始工作" << endl;
// 第二臺電腦組裝
Conputer* compute2 = new Conputer(new LenovoCpu, new LenovoVideCard, new LenovoMemory);
compute2->work();
delete compute2;
cout << "-----------------------------------" << endl;
cout << "第三臺電腦開始工作" << endl;
// 第三臺電腦組裝
Conputer* compute3 = new Conputer(new InterCpu, new LenovoVideCard, new LenovoMemory);
compute3->work();
delete compute3;
}
int main()
{
test01();
system("pause");
return 0;
}