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

C++ 成員函數不佔用對象記憶體空間的證明與應用

     編輯:關於C++

疑問起因:危險的向下轉型?

在閱讀 TAF 原始碼時,發現一段將基類指標強制轉為衍生類指標的代碼:

ServantProxy* pServantProxy = getServantProxy(...);
proxy = (typename T::element_type*)(pServantProxy); // T 為衍生類智慧指標

乍看之下極其危險:若衍生類新增資料成員,基類物件記憶體佈局較小,透過衍生類指標存取新成員將導致越界訪問。但實測發現程式正常運行,原因在於衍生類未新增任何資料成員,僅新增成員函數。

核心結論:成員函數不屬於對象記憶體佈局

透過簡單實驗驗證:

#include <iostream>
using namespace std;

class A { public: A(){} ~A(){} };
class B { public: B(){} virtual ~B(){} }; // 有虛函數,含 vptr
class C { public: C() {} ~C() {} int add(int a, int b) { return a+b; } };

int main() {
    cout << "Size(A) = " << sizeof(A) << endl; // 1 (空類別最小 1 byte)
    cout << "Size(B) = " << sizeof(B) << endl; // 8 (64位系統下 vptr 8 bytes)
    cout << "Size(C) = " << sizeof(C) << endl; // 1 (非虛函數不佔空間)
    return 0;
}

結果證明: 只有虛函數會引入虛函數表指標(vptr),佔用對象記憶體;普通成員函數(含非虛函數)完全不佔用對象存儲空間。同一類別的所有對象共享同一份函數代碼,代碼儲存在代碼段,與對象實例無關。

深度解析:this 指標與調用機制

成員函數實際上是「帶隱含 `this` 指標的普通函數」。調用 `obj.func()` 時,編譯器隱式傳遞 `&obj` 作為 `this` 參數。因此:

  • 成員函數代碼只有一份,儲存在文字段(.text section)。
  • 對象只存儲資料成員(非靜態)。
  • 衍生類若無新增資料成員,其記憶體佈局與基類完全一致,向下轉型安全。

實戰啟示:介面類與實作分離

此特性支撐了「介面類(純虛類)+ 實作類」的設計模式:

class IService { // 介面類,僅含純虛函數
public:
    virtual void doWork() = 0;
    virtual ~IService() {}
};

class ServiceImpl : public IService { // 實作類,可自由增刪非虛函數
public:
    void doWork() override { /* ... */ }
    void helper() { /* 僅內部使用,不影響記憶體佈局 */ }
};

只要不新增資料成員,`IService*` 安全轉為 `ServiceImpl*` 並調用 `helper()` 完全合法,這正是「物理上不依賴、邏輯上屬於」的生動體現。

總結

  1. 非靜態成員函數不佔用對象記憶體大小(`sizeof` 不計入)。
  2. 虛函數透過 vptr 間接影響大小,但函數本體仍在代碼段。
  3. 衍生類僅增加成員函數時,基類指標向下轉型安全,可直接存取新增函數。
  4. 設計介面類時,盡量避免在衍生類中新增資料成員,保持二進位相容與轉型安全。

理解此機制,能讓你在處理多型、轉型、二進位介面設計時更加從容不迫。

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