7. 多態 7.1 多態基本用法 1 #include<iostream> 2 using namespace std; 3 4 // 多態 5 6 // 動態多態滿足條件: 7 // 1.有繼承關係 8 // 2. 子類重寫父類的虛函數 9 // 10 // 動態多態使用 11 // 父類的指針或 ...
7. 多態
7.1 多態基本用法
1 #include<iostream> 2 using namespace std; 3 4 // 多態 5 6 // 動態多態滿足條件: 7 // 1.有繼承關係 8 // 2. 子類重寫父類的虛函數 9 // 10 // 動態多態使用 11 // 父類的指針或者引用,指向子類對象 12 // 13 14 // 動物類 15 class Animal { 16 public: 17 // 虛函數,函數地址晚綁定 18 virtual void speak() { 19 cout << "動物會說話" << endl; 20 } 21 }; 22 23 // 貓類 24 class Cat:public Animal { 25 public: 26 // 父類 虛函數 重寫 27 // 重寫:函數返回值類型、函數名、參數列表 完全相同 28 void speak() { 29 cout << "小貓在說話" << endl; 30 31 } 32 }; 33 34 // 狗類 35 class Dog :public Animal { 36 public: 37 void speak() { 38 cout << "小狗在說話" << endl; 39 40 } 41 }; 42 43 // 執行說話函數 44 // 地址早綁定,在編譯階段確定函數地址 45 // 如果想執行“貓說話”,那麼這個函數地址就不能早綁定,需要在運行階段進行綁定,即地址晚綁定 46 void doSpeak(Animal& animal) { // 父類引用接收子類對象 47 // 通過對基類定義虛函數的方法 48 // 調用speak()函數時,函數地址根據傳入的對象而動態綁定函數地址 49 animal.speak(); 50 } 51 52 void test01() { 53 Cat cat; 54 doSpeak(cat); // 父類引用接收子類對象 55 56 Dog dog; 57 doSpeak(dog); 58 } 59 60 int main() { 61 62 test01(); 63 64 system("pause"); 65 66 return 0; 67 } 68 69 // 總結 70 // 多態 71 // 分類: 72 // 靜態多態:函數重載、運算符重載 73 // 動態多態:派生類、虛函數 74 // 靜態多態與動態多態的區別: 75 // 靜態:函數地址早綁定,編譯階段確定函數地址 76 // 動態:函數地址晚綁定,運行階段確定函數地址 77 // 多態優點: 78 // 代碼組織結構清晰 79 // 可讀性強 80 // 有利於前期和後期的擴展以及維護 81 //
7.2 多態案例(一) 計算器
1 #include<iostream> 2 #include<string> 3 4 using namespace std; 5 6 // 普通實現 7 class Calculator { 8 public: 9 int getResult(string oper) { 10 if (oper == "+") { 11 return m_Num1 + m_Num2; 12 } 13 else if (oper == "-") 14 { 15 return m_Num1 - m_Num2; 16 } 17 else if (oper == "*") { 18 return m_Num1 * m_Num2; 19 } 20 } 21 22 int m_Num1; // 操作數1 23 int m_Num2; // 操作數2 24 }; 25 26 // 利用多態實現計算器 27 28 // 實現計算器抽象類 29 class AbstractCalculater { 30 public: 31 32 virtual int getResult() { 33 return 0; 34 35 } 36 int m_Num1; 37 int m_Num2; 38 }; 39 40 // 加法計算器類 41 class AddCalculator : public AbstractCalculater { 42 int getResult() { 43 return m_Num1 + m_Num2; 44 } 45 }; 46 47 // 減法計算器類 48 class SubCalculator : public AbstractCalculater { 49 int getResult() { 50 return m_Num1 - m_Num2; 51 } 52 }; 53 54 // 乘法計算器類 55 class MulCalculator : public AbstractCalculater { 56 int getResult() { 57 return m_Num1 * m_Num2; 58 } 59 }; 60 61 62 void test01() { 63 64 // 普通實現 65 Calculator c; 66 c.m_Num1 = 20; 67 c.m_Num2 = 10; 68 69 cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl; 70 cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl; 71 cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl; 72 } 73 74 void test02() { 75 // 多態使用條件 76 // 父類指針或者引用指向子類對象 77 78 // 加法運算 79 AbstractCalculater* abc = new AddCalculator; 80 abc->m_Num1 = 20; 81 abc->m_Num2 = 10; 82 cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; 83 // 堆區數據,用完後記得銷毀 84 delete abc; 85 86 // 減法運算 87 abc = new SubCalculator; 88 // 上面的delete abc只是釋放了堆區數據,abc依舊是個父類的指針 89 abc->m_Num1 = 20; 90 abc->m_Num2 = 10; 91 cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; 92 delete abc; 93 94 // 相乘運算 95 abc = new MulCalculator; 96 abc->m_Num1 = 20; 97 abc->m_Num2 = 10; 98 cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; 99 delete abc; 100 101 } 102 103 int main() { 104 105 //test01(); 106 107 test02(); 108 109 system("pause"); 110 111 return 0; 112 113 } 114 115 // 總結 116 // 在真正的開發中,提倡 開閉原則 117 // 開閉原則: 對擴展進行開放,對修改進行關閉 118 //
7.3 純虛函數和抽象類
1 #include<iostream> 2 using namespace std; 3 4 // 純虛函數與抽象類 5 6 class Base { 7 public: 8 // 純虛函數 9 // 該類為抽象類 10 virtual void func() = 0; 11 }; 12 13 class Son :public Base { 14 public: 15 void func() { 16 cout << "子類重寫了父類的純虛函數!" << endl; 17 } 18 }; 19 20 void test01() { 21 //Base b; // 報錯,抽象類,無法實例化對象 22 //new Base; // 報錯,堆區也無法實例化 23 24 //Son s; // 報錯,子類需要重寫抽象類(父類)的純虛函數 25 26 Son s; 27 s.func(); 28 } 29 30 int main() { 31 32 test01(); 33 34 system("pause"); 35 36 return 0; 37 } 38 39 40 // 總結 41 // 在多態中,通常父類中的虛函數的實現是毫無意義的,主要都是調用子類重寫的內容 42 // 因此可以將虛函數改為傳虛函數 43 // 44 // 純虛函數語法: virtual 返回值類型 函數名 (參數列表) = 0; 45 // 46 // 當類中有了純虛函數,這類也成為抽象類 47 // 48 // 抽象類特點: 49 // 無法實例化對象 50 // 子類必須重寫抽象類中的純虛函數,否則也屬於抽象類 51 // 52 //
7.4 多態案例(二) 製作飲品
1 #include<iostream> 2 #include<string> 3 4 using namespace std; 5 6 // 多態案例2 製作飲品 7 class AbstractDrinking { 8 public: 9 // 煮水 10 virtual void Boil() = 0; 11 12 // 沖泡 13 virtual void Brew() = 0; 14 15 // 倒入水中 16 virtual void PourInCup() = 0; 17 18 // 加入輔料 19 virtual void PutSomething() = 0; 20 21 // 製作飲品 22 void makeDrink() { 23 Boil(); 24 Brew(); 25 PourInCup(); 26 PutSomething(); 27 } 28 29 }; 30 31 // 製作咖啡 32 class Coffee : public AbstractDrinking { 33 public: 34 // 煮水 35 virtual void Boil() { 36 cout << "煮農夫山泉" << endl; 37 } 38 39 // 沖泡 40 virtual void Brew() { 41 cout << "沖泡咖啡" << endl; 42 } 43 44 // 倒入杯中 45 virtual void PourInCup() { 46 cout << "倒入杯中" << endl; 47 } 48 49 // 加入輔料 50 virtual void PutSomething() { 51 cout << "加入糖和牛奶" << endl; 52 } 53 }; 54 55 // 製作奶茶 56 class MilkTea : public AbstractDrinking { 57 public: 58 // 煮水 59 virtual void Boil() { 60 cout << "煮農百歲山" << endl; 61 } 62 63 // 沖泡 64 virtual void Brew() { 65 cout << "沖泡珍珠" << endl; 66 } 67 68 // 倒入杯中 69 virtual void PourInCup() { 70 cout << "倒入杯中" << endl; 71 } 72 73 // 加入輔料 74 virtual void PutSomething() { 75 cout << "加入珍珠、果粒、椰蓉" << endl; 76 } 77 }; 78 79 void doWork(AbstractDrinking* abs) { 80 abs->makeDrink(); 81 delete abs; 82 } 83 84 void test01() { 85 // 製作咖啡 86 cout << "製作咖啡" << endl; 87 doWork(new Coffee); 88 89 cout << "\n製作奶茶" << endl; 90 doWork(new MilkTea); 91 } 92 93 int main() { 94 95 test01(); 96 97 system("pause"); 98 99 return 0; 100 }
7.5 虛析構與純虛析構
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 6 class Animal { 7 public: 8 virtual void speak() = 0; 9 Animal() { 10 cout << "Animal的構造函數" << endl; 11 } 12 // 虛析構 13 //virtual ~Animal() { 14 // cout << "Animal的析構函數" << endl; 15 //} 16 17 // 純虛析構 需要聲明,也需要函數實現 18 virtual ~Animal() = 0; 19 // 有了純虛析構之後,這個類也屬於抽象類,無法實例化對象 20 21 }; 22 23 Animal::~Animal() { 24 cout << "Animal的純析構函數" << endl; 25 } 26 27 class Cat :public Animal { 28 public: 29 Cat(string name) { 30 cout << "Cat的構造函數" << endl; 31 m_Name = new string(name); 32 } 33 void speak() { 34 cout << *m_Name << "小貓在說話" << endl; 35 } 36 37 string* m_Name; 38 39 ~Cat() { 40 cout << "Cat的析構函數" << endl; 41 if (m_Name != NULL) { 42 delete m_Name; 43 m_Name = NULL; 44 } 45 } 46 }; 47 48 void test01() { 49 Animal* animal = new Cat("Tom"); 50 animal->speak(); 51 delete animal; 52 // 父類指針在析構的時候,不會調用子類中的析構函數, 53 // 導致子類如果存在堆區數據,會出現記憶體泄露情況 54 // 解決方法:父類改為虛析構 55 // 這樣就會釋放子類的記憶體(走子類的析構函數) 56 57 58 59 } 60 61 int main() { 62 63 test01(); 64 65 system("pause"); 66 67 return 0; 68 } 69 70 // 總結 71 // 虛析構與純虛析構 72 // 多態使用時,如果子類中有屬性開闢到堆區,那麼父類指針析構函數無法調用到子類的析構代碼 73 // 解決方案:將父類的析構函數改為虛析構或者純虛析構