第五章-多态性
第五章-多態性
文章目錄
- 第五章-多態性
- 多態的類型與實現
- 運算符重載
- 1、運算符重載為類的成員函數(非靜態)
- 單目運算符++的重載
- 2、運算符重載為類的非成員函數
- 其他的重載例子
- 虛函數
- 虛析構函數
- 抽象類
- Tips
多態的類型與實現
多態是指同樣的消息被不同類型的對象接收時導致不同的行為
- 多態分類
- 重載多態
- 強制多態
- 包含多態
- 參數多態
- 多態從實現的角度可以分為兩類
- 編譯時多態(靜態綁定)
- 運行時多態(動態綁定)
運行時多態要滿足三個條件:
運算符重載
運算符重載是對已有的運算符賦予多重含義,作用于不同類型的數據時有不同的行為(實質上就是函數重載)
運算符重載的過程是在編譯時完成的
重載規則:
重載規則:
1、運算符重載為類的成員函數(非靜態)
//函數參數個數比原操作數少一個,后置“++”、“--”除外 返回類型 operator 運算符(形參表) {函數體 } class A{ public:A(){}A(int m,int n):r(m),i(n){}void show(){cout<<r<<"+"<<i<<"i"<<endl;}A operator +(A &m)const{return A(r+m.r,i+m.i);}A operator -(A &m)const{return A(r-m.r,i-m.i);} private:int r,i; }; int main(){A a(2,3),b(4,5),d;d=a+b;d.show();d=b-a;d.show();return 0; }單目運算符++的重載
class A{ public:A(){}A(int m,int n):r(m),i(n){}void show(){cout<<r<<"+"<<i<<"i"<<endl;}A& operator ++(){ //前置++重載 r++;i++;return *this;}A operator ++(int){ //后置++重載 A c=*this;++(*this);return c;} private:int r,i; }; int main(){A a(2,3),d;d=a++;d.show();a.show();return 0; }2、運算符重載為類的非成員函數
//函數參數個數與原操作數一樣 返回類型 operator 運算符(形參表) {函數體 } class A{ public:A(){}A(int m,int n):r(m),i(n){}void show(){cout<<r<<"+"<<i<<"i"<<endl;}friend A operator +(const A&,const A &);friend A operator -(const A&,const A &); private:int r,i; }; A operator +(const A &c1,const A &c2) {return A(c1.r+c2.r,c1.i+c2.i); } A operator -(const A &c1,const A &c2) {return A(c1.r-c2.r,c1.i-c2.i); } int main(){A a(2,3),b(4,5),d;d=a+b;d.show();d=b-a;d.show();return 0; }其他的重載例子
1. 對<<運算符的重載
class A{ public:A(){}A(int m,int n):r(m),i(n){}friend ostream& operator <<(ostream& out,const A &n); private:int r,i; }; ostream& operator <<(ostream& out,const A &n){out<<"("<<n.r<<","<<n.i<<")";return out; } int main(){A a(2,3);cout<<a;return 0; }2. 對 [ ] 運算符的重載
class A{ public:int x,y,z;int & operator [](int index){switch(index){case 0:return x;case 1:return y;case 2:return z;}} }; int main(){A a;a[1]=10;cout<<a.y<<endl; //此時a[1]即為y return 0; }3. 對()運算符的重載
class A{ public:int x,y,z;int & operator ()(int index){switch(index){case 0:return x;case 1:return y;case 2:return z;}} }; int main(){A a;a(1)=20;cout<<a.y<<endl; //此時a(1)即為y return 0; }虛函數
虛函數必須是非靜態的成員函數
虛函數是動態綁定的基礎,經過派生之后,在類族中可以實現運行時多態
一般虛函數成員的語法:
virtual 函數類型 函數名(參數表);
class A{ public:virtual void display(); //基類虛函數 }; class B:public A{ public:void display(); //覆蓋基類的虛函數 };- 虛函數一般不聲明為內聯函數,因為對虛函數的調用需要動態綁定,而對內聯函數的處理是靜態的
- 虛函數的聲明只能出現在函數原型聲明中,不能出現在成員函數實現的時候
- 派生類覆蓋基類的成員函數時,既可以使用virtual關鍵字,也可以不使用。最好是在派生類中寫上virtual,這樣可以清楚地提示這是一個虛函數
- 如果派生類并顯式沒有給出虛函數聲明,系統會從名稱、參數、返回值三個方面進行檢查,滿足條件后派生類虛函數會自動覆蓋基類虛函數
如果將fun函數的參數類型設定為A 而不是A* ,那么三次執行的結果都是A::display()。這里運用了類型兼容規則,基類指針可以指向派生類對象,基類引用可以作為派生類對象的別名,但是基類對象不能表示為派生類對象
注意事項:
虛析構函數
C++中不能聲明虛構造函數,可以聲明虛析構函數
虛函數作為運行過程中多態的基礎,主要是針對對象的,而構造函數是在對象產生前運行的,因此虛構造函數沒有意義
一個類析構函數為虛函數,其派生類的析構函數也為虛函數。析構函數設置為虛函數后,在使用指針引用時可以動態綁定,實現運行時多態,保證使用基類指針就能調用適當的析構函數針對不同對象進行清理工作。
簡單來說,如果有可能通過基類指針調用對象的析構函數(通過delete),就需要讓基類的析構函數成為虛函數,否則會產生不確定的結果
class A{ public:virtual ~A(){cout<<"A is destructed;"<<endl;} }; class B:public A{ public:~B(){cout<<"B is destructed;"<<endl;} }; int main(){B *pb=new B();A *pa=pb;delete pa;return 0; } /* 程序運行結果為:B is destructed;A is destructed; */由于使用了許析構函數,派生類的虛構函數被調用了,動態申請的空間被正確釋放,實現了多態。
抽象類
抽象類是帶有純虛函數的類,它處于類的上層,自身無法實例化,只能通過繼承機制,生成非抽象派生類,然后在實例化
純虛函數是一個在基類中聲明的虛函數,在該基類中沒有定義具體的操作內容,由各派生類根據實際需要給出各自的定義。
語法形式為:
virtual 函數類型 函數名(參數表)=0;
細節 :
-
抽象類派生出新的類之后,如果派生類給出所有純虛函數的具體實現,派生類就不再是抽象類,可以實例化對象
-
如果派生類沒有給出全部純虛函數的具體實現,此時派生類仍然是一個抽象類
-
抽象類不能實例化,但是可以定義一個抽象類的指針和引用。通過指針或引用,可以指向并訪問這個派生類的對象,進而訪問派生類的成員,這種訪問時多態特征的。
Tips
- 指針類型
- 可以聲明指向常量的指針,此時不能通過指針改變所指對象的值,但指針本身可以改變去指向另外的對象
- 可以聲明指針類型的常量,這時指針本身的值不能被改變
- 指向函數的指針
-
函數指針就是專門用來存放函數代碼首地址的變量,一旦函數指針指向了某個函數,它與函數名便具有相同的作用
-
聲明一個函數指針時,也需要說明函數的返回值、形參列表,一般語法如下:
數據類型 (* 函數指針名)(形參表)
-
函數指針使用前要賦值,讓指針指向一個已經存在的函數代碼起始地址
函數指針名=函數名;
-
總結
- 上一篇: 第四章-数据共享与保护
- 下一篇: 第六章-template模板