首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 深度解析 C++ 函數覆寫與隱藏:虛函數多態、隱藏規則、最佳實踐

     編輯:VC 教學

前言

繼承體系中,衍生類同名函數可能構成**覆寫**、**隱藏**或**多載**,三者語意迥異。本文詳細釐清:覆寫需 virtual 且簽章完全相同;隱藏發生於非虛函數同名或參數不同;多載僅限同一類別內。附贈多層繼承覆寫/隱藏混合案例與呼叫基類隱藏函數語法。

函數覆寫 — 多態核心


class Animal {
public:
    virtual void breathe() { cout << "animal breathe\n"; }
};

class Fish : public Animal {
public:
    void breathe() { cout << "fish bubble\n"; } // 覆寫
};

構成條件 (三要素)

  1. 基類函數為 virtual。
  2. 衍生類函數**名稱、參數列表、cv-限定、回傳型別 (共變除外) 完全相同**。
  3. 發生於**基類與衍生類**之間。

運行機制

編譯器產生 **vtable**,運行時依物件動態型別決定呼叫哪個版本 (遲綁定/動態綁定)。

函數隱藏 — 靜態綁定陷阱

情況 1:基類非虛函數,衍生類同名同參


class Animal { public: void breathe() { cout << "animal breathe\n"; } };
class Fish : public Animal { public: void breathe() { cout << "fish bubble\n"; } };

Fish::breathe **隱藏** Animal::breathe。透過 Animal* 指向 Fish 物件呼叫 breathe(),**仍呼叫基類版本** (早期綁定)。

情況 2:參數列表不同 (無論基類是否 virtual)


class Base { public: virtual void fn(); };
class Derived : public Base { public: void fn(int); }; // 隱藏 Base::fn()

Derived::fn(int) 隱藏 Base::fn()。衍生類物件**無法直接呼叫** fn() (編譯錯誤:找不到無參版本)。

覆寫 vs 隱藏 vs 多載 對照表

特性覆寫隱藏多載
發生範圍基類 ↔ 衍生類基類 ↔ 衍生類同一類別內
基類需 virtual是否 (有無皆可)不適用
函數簽章完全相同同名 (參數可異)參數列表不同
綁定時機運行時 (動態)編譯時 (靜態)編譯時 (靜態)
多態效果有無無

多層繼承混合案例


class Base { public: virtual void fn(); };
class Derived : public Base { public: void fn(int); }; // 隱藏 Base::fn()
class Derived2 : public Derived { public: void fn(); }; // 覆寫 Base::fn()!
  • Derived::fn(int) 隱藏 Base::fn() (非虛)。
  • Derived2::fn() 與 Base::fn() 簽章相同 → **覆寫** Base::fn() (即使 Derived 中被隱藏)。
  • Derived2 物件透過 Base* 呼叫 fn() → 動態綁定到 Derived2::fn()。

呼叫被隱藏的基類函數


class Derived : public Base {
public:
    void fn(int) {
        Base::fn(); // 明確指定基類作用域
    }
};

語法:**BaseClassName::FunctionName(args)**。

實務判斷口訣

「同名同參看 virtual:有 virtual 是覆寫 (多態),無 virtual 是隱藏 (靜態)。同名不同參一律隱藏。多載只發生在同一個類別裡。」

完整可執行範例 (EX09.CPP 改良版)


#include 
using namespace std;

class Base {
public:
    virtual void xfn(int i) { cout << "Base::xfn(int)\n"; }
    void yfn(float f) { cout << "Base::yfn(float)\n"; }
    void zfn() { cout << "Base::zfn()\n"; }
};

class Derived : public Base {
public:
    void xfn(int i) { cout << "Derived::xfn(int)\n"; }          // 覆寫
    void yfn(int c) { cout << "Derived::yfn(int)\n"; }          // 隱藏 yfn(float)
    void zfn() { cout << "Derived::zfn()\n"; }                  // 隱藏 zfn()
};

int main() {
    Derived d;
    Base* pB = &d;
    Derived* pD = &d;

    pB->xfn(5);  // Derived::xfn(int)  (虛函數動態綁定)
    pD->xfn(5);  // Derived::xfn(int)

    pB->yfn(3.14f); // Base::yfn(float)  (靜態綁定,指標型別為 Base*)
    pD->yfn(3.14f); // Derived::yfn(int) (隱藏,float 隱性轉 int)

    pB->zfn();     // Base::zfn()       (非虛,靜態綁定)
    pD->zfn();     // Derived::zfn()    (隱藏)
}

總結

覆寫是多態基石,**必須** virtual + **完全相同簽章**。隱藏是靜態綁定陷阱,**同名即隱藏** (含參數不同)。設計介面時:基類公開函數**預設加 virtual**,衍生類覆寫**加上 override (C++11)** 讓編譯器檢查簽章一致,徹底杜絕隱藏導致的靜態綁定 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved