c++多態(tài)詳解

多態(tài)是面向?qū)ο缶幊痰囊粋€(gè)特性阳藻。它允許一個(gè)對(duì)象在不同的條件下表現(xiàn)出不同的結(jié)果晰奖。在c++中有兩種多態(tài)的表現(xiàn)類型:

  1. 編譯時(shí)多態(tài)(Compile time Polymorphism)。這個(gè)也被稱為靜態(tài)綁定(static binding)或者早期綁定(early binding)腥泥。
  2. 運(yùn)行時(shí)多態(tài)(Runtime binding)匾南。這個(gè)也被稱為動(dòng)態(tài)綁定(dynamic binding)或者遲綁定(late binding)。

<1>Compile time Polymorphism

方法的重載和操作符的重載都屬于編譯時(shí)多態(tài)的例子蛔外。接下來我們介紹下什么是方法重載蛆楞。

方法重載(function overloading)

方法重載是c++編程的一個(gè)特性溯乒。它允許我們擁有多個(gè)方法名一樣的方法,只需要這些方法的參數(shù)表是不一樣的豹爹。所謂的參數(shù)表是指參數(shù)的數(shù)據(jù)類型和排列順序裆悄。比如:

myFunc(int a, float b)的參數(shù)表是(int, float)。
myFunc(float a, int b)的參數(shù)表是(float, int)臂聋。

上面兩個(gè)的方法名雖然都是myFunc光稼,但是兩個(gè)方法的參數(shù)表是不一樣的。
總結(jié):
判斷是否符合重載條件只需要判斷同名方法的參數(shù)是否滿足參數(shù)的類型孩等,數(shù)目艾君, 順序有存在不同。
注意:
如果參數(shù)表是一樣的肄方,即使方法的返回值不一樣也是不合法的冰垄。只要參數(shù)列表是不同的,方法的返回值可以相同也可以不同权她。所以重載的條件就是需要參數(shù)列表的不同虹茶。判定不同的方法就是上面注意中提到的。
例如:

// 不合法
int sum(int, int)
double sum(int, int)

例子1

#include <iostream>
using namespace std;
class Addition {
public:
    int sum(int num1,int num2) {
        return num1+num2;
    }
    int sum(int num1,int num2, int num3) {
       return num1+num2+num3;
    }
};
int main(void) {
    Addition obj;
    cout<<obj.sum(20, 15)<<endl;
    cout<<obj.sum(81, 100, 10);
    return 0;
}

輸出:

35
191

例子2

#include <iostream>
using namespace std;
class DemoClass {
public:
    int demoFunction(int i) {
        return i;
    }
    double demoFunction(double d) {
        return d;
    }
};
int main(void) {
    DemoClass obj;
    cout<<obj.demoFunction(100)<<endl;
    cout<<obj.demoFunction(5005.516);
   return 0;
}

輸出:

100
5006.52

方法重載的優(yōu)點(diǎn)

方法重載的主要是增加了代碼的可讀性復(fù)用性(code readability and code reusability)伴奥。想象一下如果沒有方法重載這種機(jī)制写烤,我們可能要寫很多不同的函數(shù)名但是操作的本質(zhì)是一樣的翼闽。這樣代碼的可讀性就變得很差拾徙,而且也會(huì)在給函數(shù)起什么名字上面多耗精力。

方法重載的多態(tài)表現(xiàn)

從上面的例子中我們可以看到感局,方法重載是通過編譯時(shí)通過不同的參數(shù)表來確定不同的版本的函數(shù)尼啡。調(diào)用時(shí)根據(jù)參數(shù)表的內(nèi)容去找對(duì)應(yīng)的版本的方式來實(shí)現(xiàn)多態(tài)的。

<2>Runtime Polymorphism

方法的重寫就是運(yùn)行時(shí)多態(tài)的例子询微。接下來我們介紹下什么是方法的重寫崖瞭。

方法的覆蓋\重寫(function overriding)

方法的覆蓋也是c++編程的一種特性。這種機(jī)制允許我們?cè)谧宇愔袚碛幸粋€(gè)和父類一樣的方法撑毛。子類可以繼承了父類的數(shù)據(jù)成員和成員函數(shù)书聚。當(dāng)我們想要修改繼承于父類的某種方法的功能時(shí),我們就可以通過覆蓋的機(jī)制來實(shí)現(xiàn)藻雌。通過這種方式雌续,我們就好像在子類當(dāng)中創(chuàng)建了一個(gè)父類對(duì)應(yīng)該方法的新方法。

例子:

在重寫一個(gè)方法時(shí)胯杭,我們需要保證子類中的方法簽名和改寫的父類方法一致驯杜。這里所指的方法簽名是指參數(shù)的數(shù)據(jù)類型和順序。下面這個(gè)例子中做个,要覆蓋的父類方法中并沒有任何參數(shù)鸽心,因此我們子類中的重寫方法也不需要任何參數(shù)滚局。

#include <iostream>
using namespace std;
class BaseClass {
public:
   void disp(){
      cout<<"Function of Parent Class";
   }
};
class DerivedClass: public BaseClass{
public:
   void disp() {
      cout<<"Function of Child Class";
   }
};
int main() {
   DerivedClass obj = DerivedClass();
   obj.disp();
   return 0;
}

輸出:

Function of Child Class

注意:
在方法重寫中,在父類中對(duì)應(yīng)的方法我們稱之為被重寫的方法(overridden function)顽频,在子類中的方法我們稱之為重寫方法(overriding function)藤肢。

如何通過子類調(diào)用被重寫的父類方法

上面的例子我們看到了子類調(diào)用重寫的方法。那么如何通過子類來調(diào)用父類中被重寫的方法(overridden function)呢冲九?我們可以通過用父類引用指向子類的實(shí)例的方式來調(diào)用我們的父類被重寫的方法谤草。用下面的例子來幫助我們理解:

#include <iostream>
using namespace std;
class BaseClass {
public:
   void disp(){
      cout<<"Function of Parent Class";
   }
};
class DerivedClass: public BaseClass{
public:
   void disp() {
      cout<<"Function of Child Class";
   }
};
int main() {
   /* Reference of base class pointing to
    * the object of child class.
    */
   BaseClass obj = DerivedClass(); 
   obj.disp();
   return 0;
}

輸出:

Function of Parent Class

如果你想要在子類中重寫的函數(shù)(overriding function)中調(diào)用父類中被重寫的函數(shù)(overridden function),你只需要這樣做:

//父類類名::方法名
parent_class_name::function_name

如果用上面的例子來解釋上面這種用法就是:

BaseClass::disp();

虛函數(shù)

當(dāng)我們?cè)诟割愔猩昝髁艘粋€(gè)函數(shù)為虛函數(shù)時(shí)莺奸,所有子類中該方法的重寫函數(shù)都會(huì)默認(rèn)的被認(rèn)為是虛函數(shù)(不管是否有virtual關(guān)鍵字)〕蠛ⅲ現(xiàn)在的問題是我們?yōu)槭裁匆v一個(gè)函數(shù)聲明為虛函數(shù)呢?這是為了讓編譯器明白這個(gè)函數(shù)的調(diào)用必須到運(yùn)行時(shí)間才能確定灭贷。只有當(dāng)對(duì)象的類型被確定時(shí)温学,才知道調(diào)用哪個(gè)版本的函數(shù)。
接下來讓我們來看兩個(gè)例子來幫助理解甚疟。分別重寫非虛函數(shù)的父類函數(shù)和重寫虛函數(shù)的父類函數(shù)仗岖。

重寫一個(gè)非虛函數(shù)

#include<iostream>
using namespace std;
//Parent class or super class or base class
class Animal{
public:
   void animalSound(){
      cout<<"This is a generic Function";
   }
};
//child class or sub class or derived class
class Dog : public Animal{
public:
   void animalSound(){ 
      cout<<"Woof";
   }
};
int main(){
   Animal *obj;
   obj = new Dog();
   obj->animalSound();
   return 0;
}

輸出:

This is a generic Function

重寫一個(gè)虛函數(shù)

#include<iostream>
using namespace std;
//Parent class or super class or base class
class Animal{
public:
   virtual void animalSound(){
      cout<<"This is a generic Function";
   }
};
//child class or sub class or derived class
class Dog : public Animal{
public:
   void animalSound(){ 
      cout<<"Woof";
   }
};
int main(){
   Animal *obj;
   obj = new Dog();
   obj->animalSound();
   return 0;
}

輸出:

Woof

總結(jié):

很顯然,重寫虛函數(shù)的方式使得調(diào)用的時(shí)候览妖,程序是看對(duì)象的類型來決定調(diào)用的函數(shù)轧拄。在第二個(gè)覆蓋虛函數(shù)的例子中,我們可以看見雖然obj指針的指針類型是父類Animal讽膏,但是其指向的對(duì)象是子類Dog的對(duì)象檩电。因此這時(shí)候,調(diào)用的animalSound是子類中的重寫函數(shù)而不是父類里頭的該函數(shù)府树。接下來我們通過一個(gè)例子再來進(jìn)一步了解下它的特性俐末。

#include<iostream>
using namespace std;

class Base{
    public:
    Base(){
        std::cout << "Base::Base()" << std::endl;
    }
    ~Base(){
        std::cout << "Base::~Base()" << std::endl;
    }
    void print1(){
        std::cout << "Base::print1()" << std::endl;
    }
    void virtual print2(){
        std::cout << "Base::print2()" << std::endl;
    }
};

class Child:public Base{
    public:
    Child(){
        std::cout << "Child::Child()" << std::endl;
    }
    ~Child(){
        std::cout << "Child::~Child()" << std::endl;
    }
    void print1(){
        std::cout << "Child::print1()" << std::endl;
    }
    void print2(){
        std::cout << "Child::print2()" << std::endl;
    }
};

int main()
{
    
    Base* base = new Base();
    Child* child = new Child();

    base->print1();
    base->print2();
    child->print1();
    child->print2();

    delete base;
    delete child;

    Child* child2 = nullptr;
    child2->print1();
    child2->print2();

    return 0;
}

輸出:

Base::Base()
Base::Base()
Child::Child()
Base::print1()
Base::print2()
Child::print1()
Child::print2()
Base::~Base()
Child::~Child()
Base::~Base()
Child::print1()
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)//報(bào)錯(cuò)

我們通過打印的信息能夠更加清楚的了解到c++背后的運(yùn)行機(jī)制。

  1. 首先我們創(chuàng)建了兩個(gè)對(duì)象奄侠,分別是基類對(duì)象和子類對(duì)象卓箫,并分別用基類指針base和子類指針child指向它們。這個(gè)過程對(duì)應(yīng)輸出我們可以看出調(diào)用了構(gòu)造函數(shù)垄潮。其中基類的構(gòu)造函數(shù)被調(diào)用了兩次烹卒。說明在創(chuàng)建子類對(duì)象的時(shí)候,調(diào)用了子類的構(gòu)造函數(shù)弯洗,而子類的構(gòu)造函數(shù)會(huì)先默認(rèn)調(diào)用父類的構(gòu)造函數(shù)旅急,然后執(zhí)行自己的構(gòu)造函數(shù)。
  2. 通用基類和子類的指針分別調(diào)用print1和print2兩個(gè)函數(shù)涂召。這個(gè)地方?jīng)]什么問題坠非。基類指針指向基類對(duì)象果正,調(diào)用的必然是基類中的函數(shù)炎码。子類也一樣盟迟。
  3. 第三步刪除對(duì)象。這邊發(fā)現(xiàn)基類的析構(gòu)函數(shù)也被調(diào)用了兩次潦闲。和構(gòu)造函數(shù)相同攒菠,子類在調(diào)用析構(gòu)函數(shù)的時(shí)候,也會(huì)調(diào)用一次父類的析構(gòu)函數(shù)歉闰。不過不同的是調(diào)用時(shí)期辖众。析構(gòu)的時(shí)候,是在執(zhí)行完自己的析構(gòu)函數(shù)體后再調(diào)用父類的析構(gòu)和敬。
  4. 最后一個(gè)涉及到多態(tài)的知識(shí)凹炸。首先創(chuàng)建了一個(gè)子類的指針child2并賦值為空指針nullptr。第一次調(diào)用print1昼弟,通過輸出可以知道啤它,該語句可以正常執(zhí)行。并且調(diào)用的是子類的print1舱痘。說明了如果函數(shù)不是虛函數(shù)的情況下变骡,c++是直接根據(jù)指針類型來判斷調(diào)用的函數(shù)版本(到底是基類的,還是基類的版本)芭逝。第二句調(diào)用print2函數(shù)時(shí)報(bào)錯(cuò)塌碌。原因是print2是個(gè)虛函數(shù)(父類中聲明了是虛函數(shù),所有子類重寫該函數(shù)都默認(rèn)是虛函數(shù))旬盯。通過虛函數(shù)的機(jī)制我們可以知道台妆,要調(diào)用哪個(gè)版本(父類的版本還是子類的版本)需要在運(yùn)行的時(shí)候通過指針?biāo)赶虻膶?duì)象類型來確定。此時(shí)瓢捉,child2指針指向?yàn)榭罩羔槪]有指向任何實(shí)例對(duì)象)频丘,因此報(bào)錯(cuò)办成。

方法重載(function overloading)和方法重寫(function overriding)的不同

到此我們已經(jīng)理解了什么是c++編程中的方法重載和方法重寫泡态。讓我們來看看它們的不同:

  1. 方法重載是在同一個(gè)類里頭完成的。在同一個(gè)類中迂卢,我們聲明方法名相同但是參數(shù)列表不一樣的方法稱為方法重載某弦。
  2. 在方法重載時(shí),我們必須確認(rèn)函數(shù)的簽名是不同的而克。但是在方法重寫中靶壮,我們必須保證重寫方法和被重寫方法具有相同的簽名。
  3. 方法重載發(fā)生在編譯期間员萍,因此也被成為編譯時(shí)多態(tài)腾降。而方法覆蓋發(fā)生在運(yùn)行期間,因此也被稱為運(yùn)行時(shí)多態(tài)碎绎。
  4. 方法重載時(shí)螃壤,在一個(gè)類中重載的方法數(shù)量沒有限制抗果。方法重寫時(shí),一個(gè)子類對(duì)應(yīng)父類的某個(gè)方法只能被重寫一次奸晴。

參考

c++方法重載
c++方法覆蓋
重載vs覆蓋
虛函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冤馏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寄啼,更是在濱河造成了極大的恐慌逮光,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墩划,死亡現(xiàn)場(chǎng)離奇詭異涕刚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乙帮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門副女,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚣旱,你說我怎么就攤上這事碑幅。” “怎么了塞绿?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵沟涨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我异吻,道長(zhǎng)裹赴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任诀浪,我火速辦了婚禮棋返,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雷猪。我一直安慰自己睛竣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布求摇。 她就那樣靜靜地躺著射沟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪与境。 梳的紋絲不亂的頭發(fā)上验夯,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音摔刁,去河邊找鬼挥转。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绑谣。 我是一名探鬼主播准潭,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼域仇!你這毒婦竟也來了刑然?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤暇务,失蹤者是張志新(化名)和其女友劉穎泼掠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垦细,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡择镇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了括改。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腻豌。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嘱能,靈堂內(nèi)的尸體忽然破棺而出吝梅,到底是詐尸還是另有隱情,我是刑警寧澤惹骂,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布苏携,位于F島的核電站,受9級(jí)特大地震影響对粪,放射性物質(zhì)發(fā)生泄漏右冻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一著拭、第九天 我趴在偏房一處隱蔽的房頂上張望纱扭。 院中可真熱鬧,春花似錦儡遮、人聲如沸乳蛾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屡久。三九已至忆首,卻和暖如春爱榔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糙及。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工详幽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓唇聘,卻偏偏與公主長(zhǎng)得像版姑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迟郎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容