欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++學習之多態(tài)的使用詳解

 更新時間:2022年06月18日 08:24:03   作者:之一Yo  
這篇文章主要為大家詳細介紹了C++中多態(tài)的機制以及使用,文中的示例代碼講解詳細,對我們學習C++有一定的幫助,感興趣的可以了解一下

前言

最近為了完成數(shù)據(jù)庫系統(tǒng)的實驗,又復習起了《C++ Primer》,上一次看這本巨著也是大二下的六月份,那時看面向?qū)ο蟪绦蚓幊踢@一章還云里霧里的,沒有領會多態(tài)的奧妙,學完 Java 之后回頭再看這一章發(fā)現(xiàn)對多態(tài)有了更好的理解。好記性不如爛筆頭,本篇博客將會對 C++ 的多態(tài)機制做一個不太詳細的總結(jié),希望下一次不需要從頭再看一遍《C++ Primer》了 _(:з」∠)_。

多態(tài)

多態(tài)離不開繼承,首先來定義一個基類 Animal,里面有一個虛函數(shù) speak()

class Animal {
public:
    Animal() = default;
    Animal(string name) : m_name(name) {}
    virtual ~Animal() = default;

    virtual void speak() const { cout << "Animal speak" << endl; }
    string name() const { return m_name; }

private:
    string m_name;
};

接著定義子類 Dog,并重寫虛函數(shù),由于構(gòu)造函數(shù)無法繼承,所以使用 using 來 “繼承” 父類的構(gòu)造函數(shù)。和父類相比,Dog 還多了一個 bark() 方法。

class Dog : public Animal {
public:
    using Animal::Animal;

    // 可加上 override 聲明要重寫虛函數(shù),函數(shù)簽名必須和基類相同(除非返回類自身的指針或引用)
    void speak() const override { cout << "Dog bark" << endl; }
    void bark() const { cout << "lololo" << endl; }
};

向上轉(zhuǎn)型

我們在堆上創(chuàng)建一個 Dog 對象,并將地址賦給一個 Animal 類型的指針。由于指針指向的是個 Dog 對象,調(diào)用 speak() 方法時,實際上調(diào)用的是底層狗狗重寫之后的 speak() 方法,而不是基類 Animal 的 speak()。也就是說編譯時不會直接確定要調(diào)用的是哪個 speak() ,要在運行時綁定。

Animal* pa = new Dog("二哈");
pa->speak(); // 調(diào)用的是 Dog::speak
pa->Animal::speak(); // 強制調(diào)用基類的 speak

利用運行時綁定這一特點,我們將基類的析構(gòu)函數(shù)定義為虛函數(shù),這樣子類對象在析構(gòu)的時候就能調(diào)用自己的虛函數(shù)了。

雖然 pa 指向的是一個 Dog 對象,但是不能使用 bark() 方法。因為 pa 是一個 Animal 類型的指針,在編譯時編譯器會跳過 Dog 而直接在 Animal 的作用域中尋找 bark 成員,結(jié)果發(fā)現(xiàn)并不存在此成員而報錯。

要實現(xiàn)向上轉(zhuǎn)型不止能用指針,引用同樣可以實現(xiàn)。但是如果寫成以下這種形式,實質(zhì)上是調(diào)用了拷貝構(gòu)造函數(shù),會用 Dog 的基類部分來初始化 Animal 對象,和向上轉(zhuǎn)型沒有任何關(guān)系,之后調(diào)用的就是底層 Animal 對象的 speak() 方法:

Dog dog("二哈");
Animal animal = dog;
animal.speak(); // 調(diào)用的是 Animal::speak

向下轉(zhuǎn)型

要想調(diào)用底層 Dog 對象的 bark() 方法,我們需要將 pa 強轉(zhuǎn)為 Dog 類型的指針。一種方法是使用 static_cast 進行靜態(tài)轉(zhuǎn)換,另一種這是使用 dynamic_cast 進行運行時轉(zhuǎn)換。相比于前者,dynamic_cast<type *> 轉(zhuǎn)換失敗的時候會返回空指針,而 dynamic_cast<type &> 則會報 bad_cast 錯誤,因此更加安全。

Dog* pd_ = static_cast<Dog *>(pa);
pd_->bark();

if (Dog* pd = dynamic_cast<Dog*>(pa)) {
    pd->bark();
} else {
    cout << "轉(zhuǎn)換失敗" << endl;
}

作用域

子類的作用域是嵌套在父類里面的,在子類的對象上查找一個成員時,會現(xiàn)在子類中查找,如果沒找到才回去父類中尋找。由于作用域的嵌套,會導致子類隱藏掉父類中的同名成員。比如下述代碼:

class Animal {
public:
    virtual void speak() const
    {
        cout << "Animal speak" << endl;
    }
};

class Dog : public Animal {
public:
    // void speak() const override { cout << "Dog speak" << endl; }
    void speak(string word) const
    {
        cout << "Dog bark: " + word << endl;
    }
};

int main(int argc, char const* argv[])
{
    Animal* pa = new Dog();
    Dog* pd = new Dog();
    // pd->speak(); 報錯
    pd->speak("666"); // Dog::speak 隱藏了 Animal::speak
    return 0;
}

我們在父類中定義了一個虛函數(shù) void speak(),子類中沒有重寫它,而是定義了另一個同名但是參數(shù)不同的函數(shù) void speak(string word)。這時候子類中的同名函數(shù)會隱藏掉父類的虛函數(shù),如果寫成 pd->speak(),編譯器會先在子類作用域中尋找名字為 speak 的成員,由于存在 speak(string word),它就不會接著去父類中尋找了,接著進行類型檢查,發(fā)現(xiàn)參數(shù)列表對不上,會直接報錯。如果用了 VSCode 的 C/C++ 插件,可以看到參數(shù)列表確實只有一個,沒有提示有重載的同名函數(shù)。

要想通過調(diào)用基類的 speak() 方法,有兩種方法:

  • 向上轉(zhuǎn)型,使用基類的指針 pa 來調(diào)用 pa->speak(),由于子類沒有重寫虛函數(shù),所以在動態(tài)綁定時會調(diào)用父類的虛函數(shù);
  • 使用作用域符強制調(diào)用父類的虛函數(shù):pd->Animal::speak()

《C++ Primer》對名字查找做了一個非常好的總結(jié):

理解函數(shù)調(diào)用的解析過程對于理解 C++ 的繼承至關(guān)重要,假定我們調(diào)用 p->mem() (或者 obj.mem()),則依次執(zhí)行以下4個步驟:

1.首先確定 p (或 obj) 的靜態(tài)類型。因為我們調(diào)用的是一個成員,所以該類型必 然是類類型。

2.在 p (或 obj ) 的靜態(tài)類型對應的類中查找 mem。如果找不到,則依次在直接基類中不斷查找直至到達繼承鏈的頂端。如果找遍了該類及其基類仍然找不到,則編譯器將報錯。

3.一旦找到了 mem,就進行常規(guī)的類型檢查以確認對于當前找到的 mem,本次調(diào)用是否合法。

4.假設調(diào)用合法,則編譯器將根據(jù)調(diào)用的是否是虛函數(shù)而產(chǎn)生不同的代碼:

  • 如果mem是虛函數(shù)且我們是通過引用或指針進行的調(diào)用,則編譯器產(chǎn)生的代 碼將在運行時確定到底運行該虛函數(shù)的哪個版本,依據(jù)是對象的動態(tài)類型。
  • 反之,如果 mem 不是虛函數(shù)或者我們是通過對象(而非引用或指針)進行的調(diào)用,則編譯器將產(chǎn)生一個常規(guī)函數(shù)調(diào)用。

以上就是C++學習之多態(tài)的使用詳解的詳細內(nèi)容,更多關(guān)于C++多態(tài)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Matlab實現(xiàn)將圖像序列合并為視頻的方法詳解

    Matlab實現(xiàn)將圖像序列合并為視頻的方法詳解

    MATLAB是一種高性能語言,用于操縱矩陣、執(zhí)行技術(shù)計算、繪圖等。它代表矩陣實驗室。借助這個軟件,我們可以從圖像中創(chuàng)建視頻。這篇文章主要介紹了Matlab實現(xiàn)將圖像序列合并為視頻的四個方法,希望對大家有所幫助
    2023-03-03
  • QT圓形圖像剪切功能實現(xiàn)

    QT圓形圖像剪切功能實現(xiàn)

    這篇文章主要介紹了QT圓形圖像剪切,實現(xiàn)代碼包括剪切代碼,完整QML源碼,C++代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • C++使用read()和write()讀寫二進制文件

    C++使用read()和write()讀寫二進制文件

    以文本形式讀寫文件和以二進制形式讀寫文件的區(qū)別,并掌握了用重載的?>>?和?<<?運算符實現(xiàn)以文本形式讀寫文件,在此基礎上,本節(jié)將講解如何以二進制形式讀寫文件
    2023-10-10
  • C語言中單目操作符++、–的實例講解

    C語言中單目操作符++、–的實例講解

    C語言的操作符共分為算術(shù)操作符,移位操作符,位操作符,賦值操作符,單目操作符,關(guān)系操作符,邏輯操作符,條件操作符,逗號表達式,下表引用、函數(shù)調(diào)用和結(jié)構(gòu)成員這10大類,這篇文章主要給大家介紹了關(guān)于C語言中單目操作符++、–的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • C++的運算符你真的了解嗎

    C++的運算符你真的了解嗎

    這篇文章主要為大家詳細介紹了C++的運算符,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • C語言實現(xiàn)數(shù)據(jù)結(jié)構(gòu)和雙向鏈表操作

    C語言實現(xiàn)數(shù)據(jù)結(jié)構(gòu)和雙向鏈表操作

    這篇文章主要介紹了C語言實現(xiàn)數(shù)據(jù)結(jié)構(gòu)雙向鏈表操作,需要的朋友可以參考下
    2017-03-03
  • C++超細致講解隊列queue的使用

    C++超細致講解隊列queue的使用

    隊列先進先出,即只能在容器的末尾添加新元素,只能從頭部移除元素,下面這篇文章主要給大家介紹了關(guān)于C++中隊列queue用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-05-05
  • Qt串口通信開發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法

    Qt串口通信開發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法

    這篇文章主要介紹了Qt串口通信開發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法,需要的朋友可以參考下
    2020-03-03
  • C語言獲取文件長度的方法

    C語言獲取文件長度的方法

    這篇文章主要介紹了C語言獲取文件長度的相關(guān)知識,包括使用標準庫方法和使用Linux系統(tǒng)調(diào)用,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • C++中的模板template小結(jié)

    C++中的模板template小結(jié)

    這篇文章主要介紹了C++中的模板template的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03

最新評論