物件的記憶體中只保留了成員變數,除此之外沒有任何其他資訊,程式執行時不知道 stu 的型別為 student,也不知道它還有四個成員函式 setname()、setage()、setscore()、show(),c++ 究竟是如何通過物件呼叫成員函式的呢?
c++和c語言的編譯方式不同。c語言中的函式在編譯時名字不變,或者只是簡單的加乙個下劃線_(不同的編譯器有不同的實現),例如,func() 編譯後為 func() 或 _func()。
而c++中的函式在編譯時會根據它所在的命名空間、它所屬的類、以及它的引數列表(也叫引數簽名)等資訊進行重新命名,形成乙個新的函式名。這個新的函式名只有編譯器知道,對使用者是不可見的。對函式重新命名的過程叫做名字編碼(name mangling),是通過一種特殊的演算法來實現的。
name mangling 的演算法是可逆的,既可以通過現有函式名計算出新函式名,也可以通過新函式名逆向推演出原有函式名。name mangling 可以確保新函式名的唯一性,只要函式所在的命名空間、所屬的類、包含的引數列表等有乙個不同,最後產生的新函式名也不同。
#include using namespace std;
void display();
void display(int);
namespace ns
class demo ;
int main()
該例中宣告了四個同名函式,包括兩個具有過載關係的全域性函式,乙個位於命名空間 ns 下的函式,以及乙個屬於類 demo 的函式。它們都是只宣告而未定義的函式。
在 vs 下編譯源**可以看到類似下面的錯誤資訊:
小括號中就是經 name mangling 產生的新函式名,它們都以?開始,以區別c語言中的_。
__cdecl,__thiscall是函式呼叫約定,也就是告訴編譯器用於建棧,引數壓棧以及獲得返回值的規則。
有興趣的請看:
成員變數:c++會將class中的成員變數提取出來放在棧區的乙個同名的struct裡面,與struct有相同的記憶體布局和位元組對齊方式。
關於記憶體對齊,有興趣的可以點這裡:
成員函式:將成員函式提取出來放在**區,編譯成員函式時要額外新增乙個引數,把當前物件的指標傳遞進去,通過指標來訪問成員變數。
靜態成員函式:將靜態成員函式放在全域性靜態區。
下圖是c++內部編譯實現:
從上圖可以看出,成員函式最終被編譯成與物件無關的全域性函式。
如果成員函式中使用到了成員變數該怎麼辦呢?成員變數的作用域不是全域性,不經任何處理就無法在函式內部訪問。
c++規定,編譯成員函式時要額外新增乙個引數,把當前物件的指標傳遞進去,通過指標來訪問成員變數。
假設 demo 類有兩個 int 型的成員變數,分別是 a 和 b,並且在成員函式 display() 中使用到了,如下所示:
void demo::display()
那麼編譯後的**類似於:
void new_function_name(demo * const p)
使用obj.display()
呼叫函式時,也會被編譯成類似下面的形式:
// obj.display()
new_function_name(&obj);
這樣通過傳遞物件指標就完成了成員函式和成員變數的關聯。這與我們從表明上看到的剛好相反,通過物件呼叫成員函式時,不是通過物件找函式,而是通過函式找物件。編譯器通過指標來訪問成員變數,指標指向哪個物件就使用哪個物件的資料;編譯器通過指標的型別來訪問成員函式,指標屬於哪個類的型別就使用哪個類的函式。最後需要提醒的是,demo * const p中的 const 表示指標不能被修改,p 只能指向當前物件,不能指向其他物件。
關於c++中更多const的用法,請看:
C 之類的成員函式的原理
先來乙個類的成員函式的例子 includeusing namespace std class mytime void display int main 為什麼類中的成員函式不占用物件空間呢,正式因為類中的成員函式其實就是全域性函式或者說是該命名空間中的全域性函式,只不過把他放到了類中而已,他的本質轉...
C 反射類和成員函式的實現
c 反射類和成員函式的實現,需要用到stl標準模板庫 定義反射工廠類以及基類nsobject ifndef ns object factorye h define ns object factorye h include include typedef void sel void typedef s...
c 成員函式和全域性函式的區分
include using namespace std 設計乙個立方體的類,求出立方體的面積和體積 分別用全域性函式和成員函式進行判斷兩個立方體是否相同 class cube 獲取長 intget l 設定寬 void set w int w 獲取寬 intget w 設定高 void set h ...