如果你的函數不是虛擬的,那麼作爲成員函數(又名方法)沒有任何開銷。實際上,在生成的對象代碼中,不存在「成員函數」的這種概念(除非它是虛擬的,稍後更多)。所有方法都是具有特殊變量this
的函數。編譯器在編譯時知道在運行時要調用什麼方法,因此它可以生成特定於方法的目標代碼(有一些處理this
)。順便說一句,這就是爲什麼的情況下下面的代碼不會崩潰:
#include <iostream>
class Crashy {
public:
void wouldItCrash() {
std::cout << "Hey, I'm still alive!" << std::endl;
}
};
int main() {
Crashy* ptr = 0;
ptr->wouldItCrash();
return 0;
}
儘管空指針的出現,計劃陸續完成,並打印"Hey, I'm still alive!"
。
#include <iostream>
void Crashy__wouldItCrash(Crashy *this) {
std::cout << "Hey, I'm still alive!" << std::endl;
}
int main() {
Crashy* ptr = 0;
Crashy__wouldItCrash(ptr);
return 0;
}
我們不會取消引用函數指針,所以沒有問題發生:如果你想方法wouldItCrash
作爲某種功能的特定參數this
沒關係。
考慮下面的簡單程序:
#include <iostream>
void FunctionName__() {
std::cout << "Hello from function!" << std::endl;
}
class ClassName__ {
public:
void MethodName1__() {
std::cout << "Hello from method 1!" << std::endl;
}
void MethodName2__() {
std::cout << "Hello from method 2!" << std::endl;
}
};
int main() {
FunctionName__();
ClassName__ a;
a.MethodName1__();
a.MethodName2__();
return 0;
}
如果你編譯它沒有優化(只是g++ main.cpp
),然後看一下符號表(nm a.out
),你會看到
0804876d T _Z14FunctionName__v
...
0804882c W _ZN11ClassName__13MethodName1__Ev
08048858 W _ZN11ClassName__13MethodName2__Ev
那是,所有方法都變成了帶有特殊名稱的普通函數(所以不會發生衝突,參見name mangling)
虛擬功能
正如我前面所說的,有一些特殊的規則適用於虛擬功能。虛擬函數在編譯時無法解析(編譯器不知道在運行時將處理哪個派生類實例),所以它延遲到運行時。因此,爲了提供虛擬功能,每個類(當然有這種功能)都有一個名爲virtual method table(aka vtable)的特殊查找表。在這種情況下,您顯然需要支付一些內存空間:您需要一個指向vtable中函數的指針和一個指向您類的每個實例的vtable的指針。
我認爲你誤解了,所有的函數,成員或不是,他們的代碼駐留在內存中。可以將成員函數作爲具有隱式參數的常規函數進行查看(有點不準確)。 – StoryTeller
那麼數據成員函數究竟有什麼意義呢?只是爲了擁有更多可讀程序?我不是說範圍,虛擬,朋友等等。根據記憶的觀點,所有功能都是全球性的更有用嗎? –
成員函數表示數據和代碼之間的強關係,並帶有編譯器支持。而且他們也表達了這種連接簡單易讀,是的。 – StoryTeller