程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 水煮多態——對C++多態性的形象解釋

水煮多態——對C++多態性的形象解釋

編輯:C++入門知識

水是什麼形狀的?

乍一看這個問題似乎問得很沒有道理,其實仔細想想,水正是自然界中“多態”的完美體現。不是麼?用圓柱形容器裝水,那麼水就是圓柱形的;換用圓錐形 容器盛之,水則又會成為圓錐形的了。在這個過程中,我們並不需要關心水是如何改變形狀的,亦無需關心水在改變形狀的過程中具體做了哪些事情;我們所要關心 的,只是提供給它一個什麼形狀的容器,這就足夠了。

OO(面向對象)中所謂的多態性,也正是這個道理。對於一個同名的方法(Water),我們在不同的情況(Container)下對其進行調用,那麼它所完成的行為(Where_Am_I)也是不一樣的。以下我將解說的,便是C++之中對於“多態”幾種不同的實現形式。

 

函數的重載(Overload)

 

這兒是一個非常簡單的函數max,它返回兩個傳入參數中較大的那一個。

int max( int a, int b )

{

    if ( a > b )

        return a;

    else

        return b;

}

相信這段代碼的具體內容不用我解釋了,是的,這是一段非常基本的代碼。你可能會發現,這個max函數只適用於int類型的參數。那麼,如果我同時還需要一個針對double類型的max,又該怎麼辦呢?

所幸C++語言本身提供了這一功能,它允許我們在定義函數的時候使用相同的名稱——是為函數的重載。也就是說,我們可以繼續定義一個double版本的max:

double max( double a, double b )

{

    if ( a > b )

        return a;

    else

        return b;

}

然後,在我們的代碼中對這兩個函數分別進行調用:

void f( void )

{

    int a = max( 1, 2 );

    double b = max( 1.5, 2.5 );

}

這樣一來,我們無需關心調用的是哪個版本的max,編譯器會自動根據我們給定的參數類型(int或double)挑選出最適當的max函數來進行調用。

 

模板(Template)

 

函數的重載的確為我們提供了很大的方便,我們不需要關心調用哪個函數,編譯器會根據我們給定的參數類型挑選出最適當的函數進行調用。但是對於下面的情況,函數的重載就不是很適用了:

函數體代碼內容基本相同。

需要為多個類型編寫同樣功能的函數。

也就是說,我們也許需要更多版本(int、double,甚至更多自定義類型,如復數complex之類)的max,但是它們的代碼卻無一例外的都是:

if ( a > b )

    return a;

else

    return b;

這樣一來,我們需要做的事情就更傾向於一種體力勞動,而且,如是過多重復的工作也必然存在著錯誤的隱患。C++在這一方面,又為我們提供了一個解決方法,那就是模板。對於上面這眾多版本且內容基本相同的max函數,我們只需要提供一個像下面這樣函數模板即可:

template < typename T >

T max( const T& a, const T& b )

{

    if ( a > b )

        return a;

    else

        return b;

}

template是C++的關鍵字,表示它以下的代碼塊將使用模板。尖括號裡面的內容稱為模板參數,表示其中的T將在下面的代碼模板中作為一種確定 的類型使用。參數之所以使用const引用的形式,是為了避免遇到類對象的時候不必要的傳值開銷。在這個模板定義完畢之後,我們就可以像這樣使用了:

void f( void )

{

    int a = max< int >( 1, 2 );

    double b = max< double >( 1.5, 2.5 );

}

對於這段代碼,編譯器會分別將int與double填充到函數模板中T所在的位置,也就是分別為max< int >和max< double >各自產生一份max函數的實體代碼。這樣一來,就達到了與函數重載一樣的效果,但是程序員的工作量卻是不可同日而語的。

 

虛函數(Virtual Function)

 

下面來以水為例,說說虛函數提供的多態表現形式。首先我們建立一個Water類,用來表示水。

class Water

{

public:

    virtual void Where_Am_I() = 0;

};

正如單獨討論水的形狀沒有意義一樣,我們在這裡當然也不能允許Water類的實例化,所以成員函數Where_Am_I被定義為了純虛函數。下面,我們來分別定義水(Water)在瓶子(Bottle)、玻璃杯(Glass)以及湖泊(Lake)中的三種不同情況:

class Water_In_Bottle : public Water

{

public:

    virtual void Where_Am_I()

    {

        cout << "Now I'm in a bottle." << endl;

    }

};

 

class Water_In_Glass : public Water

{

public:

    virtual void Where_Am_I()

    {

        cout << "Now I'm in a glass." << endl;

    }

};

 

class Water_In_Lake : public Water

{

public:

    virtual void Where_Am_I()

    {

        cout << "Now I'm in a lake." << endl;

    }

};

這三者分別實現了成員函數Where_Am_I。然後,多態性的實現就可以通過一個指向Water的指針來完成:

void f( void )

{

    Water_In_Bottle a;

    Water_In_Glass b;

    Water_In_Lake c;

 

    Water *pWater[3];

    pWater[0] = &a;

    pWater[1] = &b;

    pWater[2] = &c;

 

    for ( int i = 0; i < 3; i++ )

    {

        pWater[i]->Where_Am_I();

    }

}

這樣,程序的運行結果是:

Now I'm in a bottle.

Now I'm in a glass.

Now I'm in a lake.

好了,如你所見,我們並不需要關心pWater指向的是哪一種水,而只需要通過這個指針進行相同的調用工作,水本身就可以根據自身的所在來選擇相應 的行為。虛函數的多態性是非常有用的,尤其是在使用C++進行Windows程序設計的時候。考慮那些不同的窗口針對用戶的相同行為而能夠做出不同反應, 也正是由於相應的消息響應虛函數的具體實現不同,方能達到這樣的效果。

水煮多態,暫且煮到這裡。這裡所談及的僅僅是C++對於多態的表現形式,而並未對文中三種技術(重載、模板、虛函數)的具體內容進行過多的解說—— 畢竟稍微一深入就難免會對某些技術細節進行大篇幅追究,譬如說到重載難免就會說到參數的匹配,說到模板又難免與泛型進行掛鉤,到了虛函數又不能不提一下VTable的東西……在這裡我一概全免,因為我的目的也就是希望通過上面幾個簡單的例子讓諸位看官能對OO本身的多態有一個感性的認識,感謝您們的閱讀。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved