最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文搞懂C++多態(tài)的用法

 更新時間:2022年04月24日 10:35:23   作者:賣寂寞的小男孩  
C++多態(tài)是在繼承的基礎(chǔ)上實現(xiàn)的,了解多態(tài)之前我們需要掌握一定的C++繼承的知識,本文將介紹C++中多態(tài)的概念,構(gòu)成條件以及用法,感興趣的可以學(xué)習(xí)一下

前言

C++多態(tài)是在繼承的基礎(chǔ)上實現(xiàn)的,了解多態(tài)之前我們需要掌握一定的C++繼承的知識,本文將介紹C++中多態(tài)的概念,構(gòu)成條件以及用法。

1.多態(tài)的概念

多態(tài),通俗來講就是多種形態(tài),具體點就是去完成某個行為,當(dāng)不同的對象去完成時會產(chǎn)生出不同的狀態(tài)。比如,在買票這一行為,普通人買票是全價買票,學(xué)生買票是半價買票,而軍人買票是優(yōu)先買票;再比如動物園的動物叫這個行為,不同的動物叫聲是不一樣的。這些都是生活中多態(tài)的例子。

2.C++中多態(tài)的分類

(1)靜態(tài)多態(tài)

靜態(tài)多態(tài)是指在編譯時實現(xiàn)的多態(tài),比如函數(shù)重載,看似是調(diào)用同一個函數(shù)其實是在調(diào)用不同的。
比如我們使用cout這個對象來調(diào)用<<時:

int i=1;
double d=2.2;
cout<<i<<endl;
cout<<d<<endl;

雖然調(diào)用的都是<<,但其實調(diào)用的是不同的操作符重載之后的函數(shù)。

函數(shù)重載在之前的文章中詳細講解過,這里就不再贅述。

(2)動態(tài)多態(tài)

動態(tài)多態(tài)也就是我們通常所說的多態(tài),本文以下內(nèi)容均為動態(tài)多態(tài)內(nèi)容。動態(tài)多態(tài)是在運行中實現(xiàn)的,

當(dāng)一個父類對象的引用或者指針接收不同的對象(父類對象or子類對象)后,調(diào)用相同的函數(shù)會調(diào)用不同的函數(shù)。

這段話也許比較繞,這里只是給出一個概念,可以結(jié)合下面的例子來進行理解。

3.多態(tài)的構(gòu)成條件

(1)舉例

我們先根據(jù)一個構(gòu)成例子來理解多態(tài)構(gòu)成的條件:

#include<iostream>
#include<string>
using namespace std;
class Person
{
public: 
    virtual void BuyTicket()
    {
        cout << "全價買票" << endl;
    }
};
class Student :public Person
{
public:
    virtual void BuyTicket()
    {
        cout << "半價買票" << endl;
    }
};
void Func(Person& p)
{
    p.BuyTicket();
}
int main()
{
    Person p1;
    Student p2;
    Func(p1);
    Func(p2);
}

我們先來看這樣一段代碼,其中子類Student繼承了父類。運行起來打印的結(jié)果是:

我們在反觀上述中動態(tài)多態(tài)的定義,用父類的引用或者指針(這里使用的是Person& p)來接收不同類型的對象(p1和p2),該引用或指針調(diào)用相同的函數(shù)(都調(diào)用了p.BuyTicket()),都調(diào)用了各自類中不同的函數(shù)(打印的結(jié)果不同)。我們將這一過程稱為動態(tài)多態(tài)。

如果我們不傳指針或者引用,那么將不構(gòu)成多態(tài)(原理會在多態(tài)原理中詳細解讀)。

(2)兩個概念

在解釋多態(tài)的構(gòu)成條件之前我們還需要了解兩個概念。

虛函數(shù)

虛函數(shù),即被virtual修飾的類成員函數(shù)稱為虛函數(shù)。

比如上面代碼中父類和子類的成員函數(shù)就是虛函數(shù)。

virtual void BuyTicket()
    {
        cout << "全價買票" << endl;
    }

關(guān)于虛函數(shù)還需要注意幾點:

1.普通的函數(shù)不能是虛函數(shù),只能是類中的函數(shù)。

2.靜態(tài)成員函數(shù)不能加virtual

總結(jié)起來就是只能是類的非靜態(tài)成員函數(shù)才能去形成虛函數(shù)。

虛函數(shù)的重寫

虛函數(shù)的重寫又稱為虛函數(shù)的覆蓋(重寫是表面意義上的,覆蓋是指原理上的):派生類中有一個根基類完全相同的虛函數(shù)(即派生類虛函數(shù)與基類虛函數(shù)的返回值類型,函數(shù)名,參數(shù)列表完全相同),稱子類的虛函數(shù)重寫了父類的虛函數(shù)。

//父類中的虛函數(shù)
virtual void BuyTicket()
    {
        cout << "全價買票" << endl;
    }
//子類中的虛函數(shù)
virtual void BuyTicket()
    {
        cout << "半價買票" << endl;
    }

兩個虛函數(shù)滿足返回值類型相同,函數(shù)名相同,參數(shù)列表相同。因此子類的虛函數(shù)重寫了父類的虛函數(shù)。

注意,只有虛函數(shù)才能構(gòu)成重寫。

(3)多態(tài)的構(gòu)成條件

多態(tài)的構(gòu)成滿足兩個條件:

1.必須通過基類的指針或者引用調(diào)用虛函數(shù)。

2.被調(diào)用的虛函數(shù)的派生類必須完成了對基類虛函數(shù)的重寫。

我們在來看上面的代碼,確實滿足該條件:

1.使用了父類引用p來調(diào)用虛函數(shù)。

2.派生類的虛函數(shù)完成了對基類的虛函數(shù)的重寫。

我們首先要明確使用多態(tài)的目的,就是使用不同的對象去完成同一個任務(wù)的時候會產(chǎn)生不同的結(jié)果。
如果我們拿掉以上任何一個條件都不會再構(gòu)成多態(tài),比如我們不使用指針或者引用去接收對象從而調(diào)用虛函數(shù),而是使用對象呢?

void Func(Person p)
{
    p.BuyTicket();
}

此時我們會發(fā)現(xiàn),打印的結(jié)果發(fā)生了變化:

這是不滿足我們的預(yù)期的,因為不同的對象傳給了p,p調(diào)用相同的函數(shù)卻打印了相同的結(jié)果。

我們還可以將更改參數(shù)列表或者將父類的virtual拿掉,發(fā)現(xiàn)依然不是我們想要的結(jié)果。

但是有兩個特殊的情況除外:

4.虛函數(shù)重寫的兩個例外

(1)協(xié)變

如果我們將父類和子類中的虛函數(shù)的返回值設(shè)為不同,可能會發(fā)生如下報錯:

協(xié)變指的是:派生類重寫基類虛函數(shù)時,與基類虛函數(shù)返回值類型不同。即基類的虛函數(shù)返回基類對象的指針或者引用,派生類虛函數(shù)返回派生類對象的指針或者引用時,稱為協(xié)變。

簡單來說就是兩者的返回值是父子關(guān)系的指針或者引用。

舉一個例子:

class A{};
class B:public A
    {};
class Person
{
public: 
    virtual A* BuyTicket()
    {
        A a;
        cout << "全價買票" << endl;
        return &a;
    }
};
class Student :public Person
{
public:
    virtual B* BuyTicket()
    {
        B b;
        cout << "半價買票" << endl;
        return &b;
    }
};

我們將上一段代碼進行了改寫,定義了B繼承A,而在Person和Student兩個類中的虛函數(shù)中將返回值分別置為A和B,由于A和B是繼承關(guān)系,所以仍然可以構(gòu)成多態(tài),我們稱派生類的虛函數(shù)為基類的虛函數(shù)的協(xié)變。

注意返回值必須是指針或者引用,對象不會構(gòu)成協(xié)變。

(2)析構(gòu)函數(shù)的重寫

首先我們先回顧一下沒有構(gòu)成多態(tài)的析構(gòu)函數(shù)調(diào)用:只需要子類對象銷毀時無需手動銷毀父類對象,會自動調(diào)用父類對象的析構(gòu)函數(shù)。

1.如果基類的析構(gòu)函數(shù)為虛函數(shù),此時子類的析構(gòu)函數(shù)無論加不加virtual,都是對父類的析構(gòu)函數(shù)的重寫。

2.雖然子類和父類的析構(gòu)函數(shù)的函數(shù)名不同,但其實編譯器對析構(gòu)函數(shù)的名稱進行了特殊的處理,都處理成了destructor。

下面舉例說明,將Person和Student寫入析構(gòu)函數(shù):

//父類中的析構(gòu)函數(shù)
   virtual ~Person()
   {
        cout << "~Person" << endl;
    }
//子類中的析構(gòu)函數(shù)
    virtual ~Student()
    {
        cout << " ~Student" << endl;
    }
//主函數(shù)
    Person* p1 = new Person;
    Person* p2 = new Student;
    delete p1;
    delete p2;    

構(gòu)成多態(tài)的結(jié)果是,Person*類型的p1和p2,接收兩個不同類型的對象即Person類型和Student類型,在調(diào)用析構(gòu)函數(shù)的時候可以分開調(diào)用(子類對象調(diào)用子類的析構(gòu)函數(shù),父類對象調(diào)用父類的析構(gòu)函數(shù)。)

我們將上述代碼運行一下,會發(fā)現(xiàn):

結(jié)果的確是如此,當(dāng)析構(gòu)父類對象時,調(diào)用父類的析構(gòu)函數(shù),當(dāng)析構(gòu)子類對象時,調(diào)用的是子類的析構(gòu)函數(shù)和父類的析構(gòu)函數(shù)。

如果我們不使用父類指針進行管理,而是使用對象來接收子類對象呢?

    Student p2;
    Person p3 = p2;

此時我們發(fā)現(xiàn)打印的結(jié)果是:

在析構(gòu)p3的時候,并沒有根據(jù)按Student類的規(guī)則來進行析構(gòu)。

同時,當(dāng)我們將派生類的virtual去掉的時候,仍然可以構(gòu)成多態(tài),這與底層原理有關(guān),在下面的介紹中會提及。為了統(tǒng)一性,不建議將virtual拿掉,C++大佬為了防止發(fā)生不必要的內(nèi)存泄漏,所以設(shè)置了這一規(guī)則。這就導(dǎo)致所有的其實派生類的所有虛函數(shù)virtual都可以省略。這是由于其繼承了基類的virtual屬性,具體的還要在底層去理解,再強調(diào)一遍,盡量不要在派生類中省略virtual。

5.final與override

(1)final

限制類不被繼承

但我們想要設(shè)計一個不被繼承的類時,目前我們知道的有一種方法:就是將父類的構(gòu)造函數(shù)設(shè)為私有(這是因為子類需要調(diào)用父類的構(gòu)造函數(shù)來進行初始化)。如果使用這種方式,定義父類對象的話需要使用單例模式。

final提供了另一種方式來限制一個類不會被繼承。

只需要在不想被繼承的類后加final即可:

class Person final
{
public: 
    virtual A* BuyTicket()
    {
        A a;
        cout << "全價買票" << endl;
        return &a;
    }
    virtual ~Person()
   {
        cout << "~Person" << endl;
    }
};

此時如果子類去繼承Person的話會報錯。

限制虛函數(shù)不被重寫

當(dāng)我們在函數(shù)后加上final的時候,該虛函數(shù)將不能被子類中的虛函數(shù)重寫,否則會發(fā)生報錯。

    virtual A* BuyTicket() final
    {
        A a;
        cout << "全價買票" << endl;
        return &a;
    }

(2)override

將override放在子類的重寫的虛函數(shù)后,判斷是否完成重寫(重寫的是否正確)

     virtual B* BuyTicket(int i=10) override
    {
        B b;
        cout << "半價買票" << endl;
        return &b;
    }

注意:final關(guān)鍵字放在父類的位置,override關(guān)鍵字放在子類的位置。

6.抽象類

在虛函數(shù)的后面加上=0,則這個函數(shù)為純虛函數(shù)。包含純虛函數(shù)的類叫做抽象類(也叫做接口類),抽象類不能實例化出對象。派生類繼承后也不能實例化出對象。**只有重寫虛函數(shù),派生類才能實例化出對象。**注意雖然不能實例化出對象,但是可以定義指針。

抽象類的存在本質(zhì)上來說就是希望我們在派生類中重寫父類的虛函數(shù)。抽象類中的虛函數(shù)一般只聲明,不實現(xiàn),因為沒有意義。我們可以搭配override來使用。

//將父類中寫入純虛函數(shù),父類變成抽象類
class Person
{
public: 
    virtual A* BuyTicket() =0//純虛函數(shù)
    {
        A a;
        cout << "全價買票" << endl;
        return &a;
    }
    virtual ~Person()
   {
        cout << "~Person" << endl;
    }
};

此時子類必須只有重寫虛函數(shù)才能定義對象。通常情況下現(xiàn)實中沒有的事物,定義成抽象類會比較合適。

雖然我們不能使用抽象類來定義對象,但是我們可以使用抽象類來定義指針。

class Car
{
public:
    virtual void Drive() = 0
    {
        cout << " Car" << endl;
    }
};
class Benz :public Car
{
public:
    virtual void Drive()
    {
        cout << "Benz" << endl;
    }
    void f()
    {
        cout << "f()" << endl;
    }
};
int main()
{
    //Car* p = nullptr;
    //p->Drive();//程序會崩潰
    Car* a = new Benz;
    a->Drive();
}

我們可以使用父類指針去接收子類對象,同時調(diào)用函數(shù)。但是不能使用父類去創(chuàng)建對象。

7.總結(jié)

C++多態(tài)的目的在于當(dāng)我們使用父類的指針或者引用去接收子類的對象后,接收不同的子類對象的父類指針或者引用調(diào)用的相同的函數(shù)產(chǎn)生的結(jié)果不同。

重點在于實現(xiàn)多態(tài)的幾個條件:

一是用父類的指針或者引用來接收。

二是子類必須對父類的虛函數(shù)進行重寫。

到此這篇關(guān)于一文搞懂C++多態(tài)的用法的文章就介紹到這了,更多相關(guān)C++ 多態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言學(xué)生成績管理系統(tǒng)設(shè)計

    C語言學(xué)生成績管理系統(tǒng)設(shè)計

    這篇文章主要為大家詳細介紹了C語言學(xué)生成績管理系統(tǒng)設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 詳解QListWidget如何實現(xiàn)自定義Item效果

    詳解QListWidget如何實現(xiàn)自定義Item效果

    這篇文章主要為大家介紹了如何通過QListWidget實現(xiàn)自定義Item效果,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-04-04
  • C++的指針,引用和STL詳解

    C++的指針,引用和STL詳解

    這篇文章主要為大家詳細介紹了C++的指針,引用和STL,使用數(shù)據(jù)庫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • STL  priority_queue(優(yōu)先隊列)詳解

    STL priority_queue(優(yōu)先隊列)詳解

    這篇文章主要介紹了 STL priority_queue(優(yōu)先隊列)詳解的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • C語言實現(xiàn)簡易撲克牌游戲

    C語言實現(xiàn)簡易撲克牌游戲

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡易撲克牌游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯誤的問題

    C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯誤的問題

    這篇文章主要介紹了std::queue中遇到釋放內(nèi)存錯誤的問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • C++ 之 Asio 庫(全面解析)

    C++ 之 Asio 庫(全面解析)

    下面小編就為大家?guī)硪黄狢++ 之 Asio 庫(全面解析)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • ??C++11系列學(xué)習(xí)之Lambda表達式

    ??C++11系列學(xué)習(xí)之Lambda表達式

    這篇文章主要介紹了??C++11系列學(xué)習(xí)之Lambda表達式,C++11終于也引入了lambda表達式,lambda最早來源于函數(shù)式編程,現(xiàn)代語言慢慢都引入了這個語法,下文關(guān)于??C++11Lambda表達式相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-04-04
  • C++實現(xiàn)LeetCode(241.添加括號的不同方式)

    C++實現(xiàn)LeetCode(241.添加括號的不同方式)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(241.添加括號的不同方式),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言之包含min函數(shù)的棧實例詳解

    C語言之包含min函數(shù)的棧實例詳解

    這篇文章主要為大家詳細介紹了C語言之包含min函數(shù)的棧,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02

最新評論

皮山县| 石河子市| 根河市| 龙州县| 阿鲁科尔沁旗| 三都| 密云县| 泗阳县| 铜陵市| 乐安县| 淄博市| 鹤山市| 大石桥市| 成安县| 佛学| 永靖县| 贡山| 方城县| 任丘市| 简阳市| 陆良县| 衡东县| 娱乐| 长寿区| 平阳县| 南安市| 满洲里市| 金川县| 霸州市| 泗阳县| 揭东县| 河北省| 河曲县| 南宁市| 三亚市| 耿马| 建昌县| 田东县| 商都县| 泰州市| 宜兴市|