首先請看下面的語句:
Point3d obj; Point3d *ptr = &obj;
當使用上述指針或者對象調用成員函數Func()時,會有:
obj.Func(); ptr->Func();
上述調用的背後到底完成了一些什麼呢?
假設Func函數的定義如下:
Point3d Point3d::Func() const { Float a = getA(); Point3d ret; ret._x = _x/a; ret._y = _y/a; ret._z = _z/a; return ret; }
getA的定義為:
float Point3d::getA() { return sqrt(_x*_x+_y*_y+_z*_z); }
那麼看過這些函數的定義之後,我們能否得知上述代碼的執行過程呢?答案是不行!上述的代碼最多能告訴我們Func函數一定不是static。(因為它操作了non-static成員變量,以及是const函數)。
那麼上面這樣的成員函數的調用過程到底是怎麼完成的呢?
C++的設計准則之一是:non-static成員函數的調用與非成員函數的調用的效率應該是一致的。選擇將函數聲明為成員函數是不應
該有任何的額外負擔的。於是乎編譯器在調用成員函數的時候是將其視為非成員函數來調用的。
例如:一個getA的非成員函數的定義為:
float getA(const Point3d *this) { return sqrt(this->_x* this->_x + this->_y * this->_y + this->_z * this->_z); }
這樣的函數會給我們一個錯覺,那就是非成員函數的調用是效率較低的,因為它是間接地去得到對象的各個成員變量的。而在成員函數裡面我們是直接使用的。其實這是一個很大的誤區。
這裡其實可以很簡單的理解一下,類的成員函數其實是有一個隱含的形式參數的,那就是this指針。然而編譯器就是這麼做的,編譯器按照下面的步驟來處理成員函數的調用的:
1 改寫成員函數的函數原型,將那個隱含的this指針表示出來。提供一個存取的管道,也就是說我們的函數中調用的就是這個形參(this)的成員變量。如下:
Point3d Point3d::Func ( Point3d * const this)
注意此處const的位置,this指針本身是const的。如果該成員函數本身也是一個const成員函數的話,那麼該函數原型應該是
Point3d Point3d::Func ( const Point3d * const this)
2 將該函數中的成員變量使用this指針來進行間接存取。例如:
{
return sqrt(this->_x* this->_x + this->_y * this->_y + this->_z * this->_z);
}
3 將成員函數重新書寫為一個外部函數,但是這裡值得注意的一個技術“mangling”這個是C++裡用於處理重載的同名函數的一個技術。使得該函數名字在程序中是獨一無二的。
上述的Func函數或許會處理為下面的名字:(不同的編譯器處理方式不同)
Extern Func_Point3dFv(Point3d *const this);
OK!大功告成,現在上面的 obj.Func()的調用就變成了 Func_Point3dFv(&obj)。
這裡的整個過程都是編譯器在訴說 “成員函數的調用的效率必須與非成員函數一致”。
[updated] 使用類作用域符號調用成員函數(無論是虛函數還是非虛函數),編譯器的決議方式與non-static成員函數是一致的。
使用對象調用成員函數(無論是虛函數還是非虛函數),編譯器的決議方式與non-static成員函數是一致的。