member functions的呼叫方式
- c++支持三種型別的member functions:static、nonstatic、virtual,且每一種呼叫方式不盡相同
nonstatic member functions
- nonstatic member function至少和nonmember function有著相同的效率
? 現有如下函式呼叫:
float do(const A *_this) {...}
float A::do() const {...}
//第一個函式轉換
float do(const A *_this)
{
return sqrt(
_this->x * _this->x + _this->y * _this->y + _this->z * _this->z
)
};
//第二個nonstatic member function轉換成上面相同的形式nonmember function
-
nonstatic member function轉化為 nonmember function步驟:
-
改寫函式原型以安插一個額外引數到member function,將提供class object存取渠道,此引數也就是this指標,若member function為const,this也需加個const
float do(const A* const _this) -
將每個對nonstatic data member的存取操作改為this指標
_this->x * _this->x + _this->y * _this->y + _this->z * _this->z -
將member function經過mangling處理重新寫為一個外部函式,且名稱是獨一無二的
extern do_7AFv( A* const this );
-
-
一般而言,data member的名稱前會加上class名稱,形成獨一無二的命名;而member function則還需加上引數鏈表
class B { public: int val; ... }; //對val進行name mangling val_3; ----------------------- class C { public: void x(float newX); float x(); } //member function進行name mangling void x__5CFf(float newX); float x__5CFv();
virtual member functions
? 現有如下代碼:
class A
{
virtual A do1() const;
virtual float do2() const;
}
A a;
A* pt = &a;
pt->do1();
//轉化
( *pt->vptr[1] )(pt);
float d = do2();
//轉化
float d = ( *this->vptr[2] )( this );
a.do1();
//轉化
( *a.vptr[1] )( &a );
? ( * pt -> vptr [1] )( pt )其中:1為virtual table slot的索引值,關聯到virtual member function,也就是do1()
? ( *a.vptr[1] )( &a ) 沒有必要,應該這樣呼叫:A::do1()
-
經由class object呼叫virtual function優化跟nonstatic member function一樣
-
為支持virtual function機制,需要能對多型物件進行執行期型別判斷,將必要資訊加在指標或參考上,而必要資訊則有:
- 指標或參考指向的物件的地址
- 物件型別的結構的地址
-
多型表示用一個public base class的指標或參考尋址一個derived class object
Point* ptr; ptr = new Point2d;-
當前ptr被稱為消極的多型形式,可以在編譯時期完成(virtual base class除外);當ptr指出的具體物件被使用時才是積極的
ptr->z(); -
class是否支持多型,唯一方法是看其是否有virtual function
-
-
實作多型,我們需要在每個class object上增加兩個members:
- 一個字串或數字,表示class型別
- 一個指標,指向一表格,表格中含有virtual functions執行期地址
-
隨后只需兩步即可找到其地址:
- 每個class object安插一個由編譯器生成的指標,該指標指向表格
- 每個virtual function被指派一個表格索引值
-
一個class只有一個virtual table,每個table內含對應的class object中的active virtual functions實體地址,而active virtual functions又包括:
- 這一class定義的函式實體
- 繼承自base class的函式實體
- 一個pure virtual called函式
-
對于多重繼承,銷毀物件時若呼叫delete需要指向derived class object的起始處,但因為指標或參考真正所指的物件在執行期才可以確定,所以offset無法在編譯期求得,針對多重繼承這種情況,derived class需要內含n-1個額外的virtual tables(n表示其上一層base classes個數),那么如何支持一個class擁有多個virtual tables呢?只需將每一個tables以外部物件的形式產出,并賦予獨一無二的名稱
-
不要在virtual base class中宣告nonstatic data members
static member function
- static member function特性:
- 沒有this指標,成為一個callback函式
- 不可直接存取class中的nonstatic members
- 不可被宣告為const、volatile、virtual
- 不需要經由class object呼叫
- static member function同樣也有name mangling
- 對static member function取地址,得到的是記憶體中的地址;由于沒有this指標,static member function地址型別是一個nonmember函式指標
指向member functions 的指標
-
指向member function的指標和指向member selection operator的指標,其作用是作為this指標的空間保留者,這也說明了為什么static member function的指標型別是函式指標,畢竟其沒有this指標
-
使用member function指標,若不用于virtual function、多重virtual繼承、virtual base class,其成本跟用nomember function指標差不多
-
virtual member function的地址在編譯期是未知的,我們所能知道的僅是virtual function在其相關之virtual table的索引值
inline functions
-
inline關鍵詞只是一個請求,若此請求被編譯期接受,編譯期則認為其可以用一個運算式將函式展開
-
inline函式的復雜度通過計算assignments、function calls、virtual function calls等操作的次數以及每個運算式種類的權值綜合決定
-
若函式因其復雜度或建構問題,被判斷不可稱為Inline,那么此函式將被轉換為static函式,并在"被編譯模塊"內產生對應的函式定義
-
inline function展開期間,做了以下三件事:
-
每個形參都被對應的實參取代,但這其中可能會導致實際引數的多次求值,面對這種情況,需要引入臨時物件,比如,若實際引數是常量運算式,在替換前先引入臨時物件,常量運算式求值后賦值給臨時物件,后繼inline替換只需使用臨時物件
inline int min( int i, int j ) { return i < j ? i : j; } inline int bar() { int minval; minval = min( foo(), bar() + 1 ); return minval; } //minval = min( foo(), bar() + 1 )展開 int t1, t2; minval = ( t1 = foo() ), ( t2 = bar() + 1 ), t1 < t2 ? t1 : t2; -
若內含區域變數,則需將區域變數放在函式呼叫的一個封閉區段,且擁有一個獨一無二的名稱,因為,如果Inline以單一運算式的方式擴展多次,每次擴展都需要自己的區域變數,特別是還含有副作用引數,可能會導致大量臨時性物件產生;但如果是分離成多個式子擴展多次,只需一組區域變數即可重復使用
inline int min( int i, int j ) { int minval = i < j ? i : j; //區域變數 return minval; } { ... //minval = min(val1, val2); int __min_lv_minval; minval = (__min_lv_minval = val1 < val2 ? val1 : val2), __min_lv_minval; } -
inline函式提供強有力的工具,但于noninline函式相比還是需要更小心地處理
-
-
盡量不要Inline中套inline,可能會使簡單的Inline因其連鎖復雜度而沒辦法展開
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/518847.html
標籤:其他
