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

C++ 虛函數深度解析:從內存佈局到多態實現原理

     編輯:關於C++

一、基本概念

C++ 通過虛函數實現多態:「無論發送消息的對象屬於何類,均發送同一形式消息,處理方式隨接收對象而異」。在類層次結構中,可對任意派生類對象的同名函數調用,具體處理隨所屬類而變。虛函數是可在派生類中重新定義賦予新功能的成員函數。

二、虛函數定義與重定義語法

class Base {
public:
    virtual void Fun(); // 基類聲明虛函數
};

class Derived : public Base {
public:
    virtual void Fun(); // 派生類重定義(可加 virtual,也可不加)
};

三、虛函數在內存中的結構

1. 單一虛函數的類大小

class A { public: virtual void fun0() {} };
int main() { A a; cout << sizeof(a); } // 輸出 4 (32位系統指針大小)

2. 多個虛函數不增加對象大小

增加 virtual void fun1(),sizeof(A) 仍為 4。因為虛函數地址存放在虛函數表(VTABLE)中,對象僅包含一個指向 VTABLE 的指針(vptr)。

3. 無虛函數時大小為 1

class A { public: void fun0() {} }; // 非虛
int main() { A a; cout << sizeof(a); } // 輸出 1 (空類最小佔 1 字節)

4. 帶數據成員時的大小計算

class A { public: virtual void fun0() {}; int a; int b; };
// sizeof(A) = 12 (vptr 4 + int a 4 + int b 4,可能含對齊填充)

虛函數表結構圖1

圖一:對象包含 vptr 指向虛函數表

5. 多重繼承下的虛函數表佈局

class A { public: virtual void f() {} };
class B { public: virtual void f() {} };
class C { public: virtual void f() {} };
class Drive : public A, public B, public C {};
// sizeof(Drive) = 12 (三個基類各有一個 vptr)

多重繼承虛表結構

圖三:多重繼承包含多個 vptr

四、虛函數實現多態與動態綁定

1. 多態演示

class A { public: virtual void f() { cout << "A::f"; } };
class B : public A { public: virtual void f() { cout << "B::f"; } };
class C : public A { public: virtual void f() { cout << "C::f"; } };
class D : public C { public: virtual void f() { cout << "D::f"; } };

int main() {
    A a; B b; C c; D d;
    a.f(); b.f(); c.f(); d.f(); // 分別輸出 A::f B::f C::f D::f
}

注意:多態 ≠ 函數重載。

2. 動態綁定實例

void test(A* a) { a->f(); } // 參數為基類指針

int main() {
    B* b = new B; C* c = new C;
    char choice;
    do {
        cout << "type B or C: "; cin >> choice;
        if (choice == 'b') test(b);
        else if (choice == 'c') test(c);
    } while(1);
}

運行時根據實際對象類型調用對應虛函數,即動態綁定。若去掉 virtual,則為靜態綁定(調用基類版本)。

五、基類調用派生類函數(需虛函數)

class A {
public:
    virtual void fun() { cout << "A::fun"; }
    void show() { fun(); } // 基類內部調用虛函數
};
class B : public A {
public:
    virtual void fun() { cout << "B::fun"; }
};
int main() {
    A a; a.show(); // 輸出 A::fun
    // 若 A* pa = new B; pa->show(); 則輸出 B::fun
}

利用虛函數,基類可調用派生類覆寫的函數。非虛函數則無法實現此行為。

六、指針轉換與虛函數調用規則

  • 派生類指針 → 基類指針:隱式轉換,調用虛函數時表現多態。
  • 基類指針 → 派生類指針:需 static_cast 顯式轉換,轉換後可調用基類與派生類成員。
  • 非虛函數情況下,基類指針只能調用基類版本。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved