物件導向理論中的3個術語:物件、方法和訊息。物件(object):不言而喻,它是構成系統的基本單位,有屬性和行為兩個要素,在c++中,每個物件都是由資料和函式這兩部分組成的,資料即是物件的屬性,行為稱之為方法(method),方法是對資料的操作,通常由函式實現。呼叫物件中的函式就是向該物件傳送乙個訊息(message),所謂「訊息」,其實就是乙個命令。例如:
stud.display();
就是向物件stud發出的乙個「訊息」,通知它執行其中的display「方法」(即display函式)。即:stud是物件,display()是方法,語句「stud.display();」是訊息。
1.多型性(polymorphism)
多型性定義:由繼承而產生的相關的不同的類,向其物件傳送同乙個訊息,不同的物件接收到後會產生不同的行為(即方法)。
多型性分為兩類:靜態多型性和動態多型性。函式過載和運算子過載實現的多型性屬於靜態多型性,在程式編譯時系統就能決定呼叫的是哪個函式,因此靜態多型性有稱為編譯時的多型性。靜態多型性是通過函式的過載實現的(運算子過載實質上也是函式過載)。動態多型性是在程式執行過程中才動態地確定操作所針對的物件,故稱之為執行時的多型性。動態多型性是通過虛函式實現的。
關於靜態多型性和動態多型性,請看下面的例子:
定義3個類:點、圓和圓柱
#include
//定義point基類
class point ;
point::point(float a, float b)
ostream & operator <<(ostream &output, const point &p)
void point::display()
//定義circle基類
class circle: public point ;
circle::circle(float a,float b,float r):point(a,b),radius(r)
float circle::area( ) const
ostream & operator <<(ostream &output, const circle &c)
void circle::display()
//定義圓柱體類
class cylinder: public circle ;
cylinder::cylinder(float a,float b,float r,float h):circle(a,b,r),height(h){}
float cylinder::area( ) const
float cylinder::volume() const
ostream & operator <<(ostream & output, const cylinder &cy)
void cylinder::display()
主函式(1),靜態關聯
int main()
由該主函式可知:1. 圓柱物件cy1可以直接賦值給其基類的物件;2. circle類和cylinder類中都有乙個area( )函式,之所以在cylinder中用area( )能直接呼叫cylinder::area( )而沒有呼叫circle:: area( )是因為「同名覆蓋」的緣故,預設cylinder中的area( )覆蓋了基類中的area( )函式(如果不想覆蓋,可以用純虛函式,能夠對基類函式重新定義,但是哪個效果更高還不好說)。3. 三個類中都包含了同名的過載運算子「<<」函式,但是他們的第二個引數型別互不相同,所以不能看做是同名覆蓋,實際上,是屬於靜態關聯。「<<」運算子之所以能準確地呼叫不同類中的過載函式,是因為系統在編譯時就已經確定了呼叫物件。
主函式(2),動態關聯:
int main( )
首先應該明確一點:定義為指向point基類物件的指標,當改變方向,指向派生類物件後,它僅指向派生類物件中基類的部分物件(例如當pt=&c1後,呼叫pt->display()相當於呼叫pt->point::display()),所以上面呼叫的display()函式只能輸出基類物件的值(即:定義為point型別的指標pt根本就無法指向派生類增加的資料或函式,例如pt-> circle::display()就會出錯,提示「'circle' : is not a member of 'point'」)。
要想pt能指向circle::display(),就必須用虛成員函式來實現,即把基類中的display()函式宣告為virtual型別。基類中的display()函式宣告為了virtual型別,代表了它可以在派生類中被重新定義,為它賦予新功能(所以可以基類中的虛函式的函式體可以為空,或者寫成純虛函式的形式),注意是「重新定義」而不是「共存」,即此時circle中定義的display()函式不再看做是增加的部分,而是看做基類的部分,所以直接用pt->display()或者pt->point::display()就可以呼叫circle類中定義的display()函式了,寫成pt-> circle::display()反而會出錯。
2. 虛函式
上面已經說了,c++的動態多型性是通過虛函式來實現的。「虛成員函式」簡稱「虛函式」,c++不允許在類外宣告虛函式。「虛函式允許派生類取代基類所提供的實現。編譯器確保當物件為派生類時,取代者(譯註:即派生類的實現)總是被呼叫,即使物件是使用基類指標訪問而不是派生類的指標。」
上面的例子,寫成虛函式的形式:
class
//宣告為空的虛函式 }
int mai()
也寫成純虛函式的形式:
class
int mai()
因為純虛函式「徒有其名,而無其實」,所以包含純虛函式的類都只作為基類,相當於提供一種基本的型別,它的不能被初始化,這種類被稱為「抽象基類」,它總是被呼叫的。
例如,可以給點、圓和圓柱體定義乙個抽象基類shape(形狀):
class shape
//虛函式
virtual float volume() const //虛函式
virtual void shapename() const =0; //純虛函式 };
3. 虛析構函式
如果用new運算子建立了臨時物件,若基類中有析構函式,並且定義了乙個指向該基類的指標變數。在程式用帶指標引數的delete運算子撤銷物件時,會發生乙個情況:系統會只執行基類的析構函式,而不執行派生類的析構函式。例如:
#include
//定義point基類
class point
; //定義建構函式
~point() ;
class circle: public point ;
~circle() ;
int main( )
希望用delete釋放p所指的空間,但執行結果卻為:
point ok!
表示只執行了基類piont的析構函式,而沒有執行派生類circle的析構函式。如果希望能執行派生類中的析構函式,可以將基類的析構函式宣告為虛函式,如
virtual ~ point() { cout<<"point ok!"<
如果將基類的析構函式宣告為虛函式,由該基類所派生的所有派生類的析構函式也自動成為虛函式,即使它們名字不同。可見原理和格式與上面所說的虛函式是一樣的。執行結果為:
point ok!
circle ok!
專業人員一般都習慣宣告虛析構函式,即使基類並不需要析構函式,也顯式地定義乙個函式體為空的析構函式,以保證在撤銷動態儲存空間時能得到正確的處理。
**:
C 虛函式與多型
1.1 虛函式概念 1.定義 在乙個類的成員函式前面加上virtual關鍵字,則該函式就稱為虛函式。2.如果乙個函式不是類的成員函式,則該函式不能定義為虛函式。即就是類外面不能使用virtual關鍵字 1.2 純虛函式與抽象類 1.純虛函式 在虛函式的後面加上 0 virtual void disp...
c 多型與虛函式
多型按字面的意思就是多種形態。當類之間存在層次結構,並且類之間是通過繼承關聯時,就會用到多型。c 多型意味著呼叫成員函式時,會根據呼叫函式的物件的型別來執行不同的函式。下面的例項中,基類 shape 被派生為兩個類,如下所示 include using namespace std class sha...
C 多型與虛函式
這一篇介紹一下 c 物件導向三大特徵之一的多型 之前面試某大廠的實習生被問到多型,後來又了解到一些設計模式,才體會到多型的強大,在這裡把對多型的一點點淺顯認識總結一下 虛表 class test cout sizeof test endl test p new test p vfunc 將類指標p強...