一、繼承
前言
在c++里面,繼承是指2個類之間的關系
例如:有一個org類,功能很完善,體量很大,突然有一天,需求發生改變,org類不能滿足新的需求,我們的第一想法是,復制 org 類代碼,粘貼成 new 代碼,然后在new代碼里面寫新的函數,以滿足新需求,但是我們有時無法復制粘貼,有可能體量太大,復制不方便。有可能該類我們沒有源代碼,沒有辦法去復制,所以我們應該使用繼承,讓new類繼承自 org類,一旦繼承過后,new類即使什么都不寫,就會天然的擁有org類里面所有的數據和函數
在繼承里面:
org類:? 我們稱為父類 或者 基類
new類:我們稱為子類 或者 派生類
1、繼承的語法格式
class org{
}
class new:public/protected/private org{
}
2、三種繼承方式
????????繼承類時有 public / protected / private 三種繼承方式
1)不同繼承方式下基類的訪問權限
原有權限? \? 繼承方式 | public | protected | private |
public | public | protected | private |
protected | protected | protected | private |
private | private(被隱藏) | private(被隱藏) | private(被隱藏) |
在繼承中 protected 和 private 的區別
在繼承過后,派生類無法內部訪問基類的私有成員,但是派生類可以內部訪問基類的受保護成員
?2)繼承的本質
其實繼承就相當于復制粘貼,只是復制粘貼這一步是由編譯器在編譯那一步完成的
3、繼承到底繼承哪些內容
1)可以繼承的內容
運算符重載函數、靜態成員屬性、靜態成員方法、構造函數和析構函數
????????構造函數和析構函數要特別注意,雖然派生類繼承了基類的構造函數和析構函數,但是并不是說將基類的構造函數和析構函數繼承給派生類作為構造函數和析構函數,只是繼承在派生類中,在構造和析構基類的對象時可以調用。?
2)不能繼承的內容
友元函數:是關于類的一種關系,并不是類的成員,自然不能繼承
4、初始化及訪問基類私有成員
1)初始化基類的私有成員
私有成員不論是派生類還是基類,都只能類中訪問,構造類中的成員也只能由自己的構造函數完成
因此可以在派生類的初始化列表中調用基類的構造函數來初始化。
注意:派生類只能調用直接基類的構造函數,不能夸輩調用構造函數。
例如:A繼承B,B繼承C,A可以調用B的構造函數,但是不能調用C的構造函數
2)訪問基類私有成員?
外部訪問類的私有成員需要類中設有公開接口才能訪問,派生類訪問基類的私有成員也是一樣的
5、基類、派生類、派生類中的對象的構造順序
在構建派生類對象時,按照執行順序,會先構造基類,再構建派生類中的對象,最后是派生類自己
構造的順序是:
基類 -> 派生類中的對象 -> 派生類本身
析構的順序是:(遵循先進后出原則,先構建的后析構)
派生類本身 -> 派生類中的對象 -> 基類
6、基類、派生類中的重名函數或變量
7、繼承關系
1)多級繼承
2)繼承組
3)多重繼承
4)菱形繼承
8、多重繼承
9、菱形繼承
1)菱形繼承存在的問題
2)virtusl(虛繼承)
二、多態
前言
多態是指,基類可以調用多個不同派生類對象的成員方法的能力
1、多態的實現
1)多態是通過virtual (虛函數)?來實現的(步驟)
- 聲明虛函數:在基類中用關鍵字virtual 聲明虛函數
- 重寫虛函數:將派生類中要調用的函數名重寫,與虛函數名同名同參
- 通過引用/指針調用:使用基類類型的指針或引用指向派生類的對象,并調用基類的虛函數
2)示例
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <vector>
#include <memory>
#include <unistd.h>using namespace std;class Core{private:public:virtual void func()//聲明虛函數{cout << "基類函數" << endl;}
};class Core_pro:public Core{private:public:void func()//將想要調用的函數寫為和虛函數同名同參的函數{cout << "派生類函數1" << endl;}
};class Core_pro_max:public Core{private:public:void func()//將想要調用的函數寫為和虛函數同名同參的函數{cout << "派生類函數2" << endl;}
};class Person{private:public://以引用類型接收變量,調用不同派生類的成員方法void work1(Core& c){c.func();}//以指針類型接收變量,調用不同派生類的成員方法void work2(Core* c){c->func();}
}; int main(int argc,const char** argv)
{Core c;Core_pro cp;Core_pro_max cpm;Person zs;//傳入變量zs.work1(c);zs.work1(cp);zs.work1(cpm);//傳入地址zs.work2(&c);zs.work2(&cp);zs.work2(&cpm);return 0;
}
3)多態作業練習:
1:寫一個Msg消息隊列類,要求有以下功能Msg m() 構造函數,創建消息隊列m[1] << "hello" 向消息隊列的1頻道寫入數據"hello",當然可以是其他的字符串數據m[2] >> str :從2頻道讀取消息,并且將讀到的消息存入str中析構函數:刪除消息隊列
2:寫一個員工類 Employee,有一個多態函數叫做 getSalary有一個 Cleanner 保潔類,繼承自員工類:每個月獲得 5000 工資有一個 Coder 程序員類,繼承自員工類,每個月獲得 10000工資有一個 Manger 經理類,繼承自員工類,每個月獲得 15000工資寫一個發工資的函數,要求,能夠為所有員工發放工資,即使追加新的崗位,也不會改變這個函數的邏輯
3:寫一個基類叫做顏色寫3個派生類,紅綠藍以及需要存在的其他顏色,繼承自顏色類要求實現以下功能:Red rGreen g;Color new_color = r + gnew_color.show() ;// 終端輸出 "黃色"
2、多態的原理
1)解析
- 多態的過程,其實是在基類的作用域中找到派生類的成員方法的過程
- 在基類中寫入虛函數后,基類會含有一個指針,就是虛函數指針,該指針指向虛函數列表;
- 繼承,過程中,編譯器會將基類深拷貝到派生類中,自然也會拷貝虛函數列表;
- 重寫虛函數(覆蓋),編譯器會將派生類的虛函數,覆蓋到基類的虛函數列表中;
- 查找成員方法時,由于派生類的虛函數被重寫到基類中,自然就只能查找到基類中的虛函數
2)多態的總結
- 基類有虛函數,就會有虛函數列表,所有虛函數都存放在虛函數列表里面
- 派生類繼承自基類的時候,同時繼承了虛函數列表
- 派生類重寫基類虛函數的時候,就會將虛函數列表中的基類的虛函數給覆蓋
為什么會覆蓋:因為同名同參,又在同一作用域中,不覆蓋會出現問題,也因此派生類重寫基類虛函數的行為,也叫覆蓋(2個名字,可以叫重寫,可以叫覆蓋)
3)函數重載和函數重寫的區別
- 函數重載:要求寫在同一個作用域中,函數重寫:要求在不同作用域中
- 函數重載:要求同名不同參,函數重寫:要求同名同參
- 函數重載:是靜態多態,又被稱作編譯期多態
函數重寫:是動態多態,又被稱作運行期多態
3、多態的繼承方式
多態的繼承方式:公開繼承(public)(想要多態,就必須使用公開繼承)
多態訪問派生類函數:
實際上是通過派生類中,指向基類內部虛函數列表的指針,去訪問的;
并且虛函數列表的訪問權限也是收到繼承權限的限制的;
因此,虛函數列表,必須是公開繼承,才能實現多態
4、多態中的析構函數
5、純虛類
1)純虛類的特點
2)純虛類的創建
創建純虛類只需要保證類中,至少擁有一個純虛函數
(1)什么是純虛函數
純虛函數是沒有定義的,不能被調用的
(2)怎么設置純虛函數
- 將函數設置為虛函數
- 直接將 "聲明部分" =0 即可
3)純虛類的作用
6、設計模式
設計模式:其實就是針對多態,各種方式的公式化運用
1)設計模式的五大原則
(1)接口隔離(獨立)原則
(2)開閉原則
(3)單一原則
(4)依賴倒置原則
(5)里氏替換原則
2)策略模式
3)代理模式
4)觀察者模式
5)簡單工廠模式