在程式設計工作中常會遇到在乙個「類」中通過函式指標呼叫成員函式的要求,如,當在乙個類中使用了c++標準庫中的排序函式qsort時,因qsort引數需要乙個「比較函式」指標,如果這個「類」使用某個成員函式作「比較函式」,就需要將這個成員函式的指標傳給qsort供其呼叫。本文所討論的用指標呼叫 「類」的成員函式包括以下三種情況:
(1).將 「類」的成員函式指標賦予同型別非成員函式指標,如:
例子1#include
typedef void (*function1)( ); //定義乙個函式指標型別。
function1 f1;
class test1
; //
void memberfun2()
//…};
int main()
(2) 在乙個「類」內,有標準庫函式,如qsort, 或其他全域性函式,用函式指標呼叫類的成員函式。如:
例子2:
#include
class test2
void memberfun()
//…};int main( )
//成員函式1呼叫成員函式//2。
void memberfun2( ) //成員函式2。
void memberfun3( ) // 編譯出錯
//…};
int main( )
//原成員函式1呼叫成員函式//2。
void memberfun2( ) //原成員函式2。
void memberfun3( )
int main( )
第1、 2種情況和第3種情況完全相同。
由此可以的得出結論,以上三種情況編譯不能通過的原因表面上並不在於函式型別呼叫不對,而是與 「類」有關。沒通過編譯的情況是用函式指標呼叫了 「類」的成員函式,通過編譯的是用函式指標呼叫了非成員函式,而函式的型別完全相同。那麼, 「類」的成員函式指標和非成員函式指標有什麼不同嗎?
在下面的程式中,用sizeof()函式可以檢視各種「類」的成員函式指標和非成員函式指標的長度(size)並輸出到螢幕上。
#include "stdafx.h"
#include
#include
class test; //乙個未定義的類。
class test2 //乙個空類。
;class test3 //乙個有定義的類。
//成員函式1呼叫成員函式//2。
void memberfun2( );//成員函式2。
//…};
class test4: virtual test3 ,test2 //乙個有virtual繼承的類(derivative class)。 };
class test5: test3,test2 //乙個繼承類(derivative class)。 };
int main()
改變後的**編譯順利通過。原因是,static 型別的成員函式與類是分開的,其函式指標也不包含物件資訊,與一般函式指標一致。這種方法雖然簡便,但有兩個缺點:1、被呼叫的函式成員定義內不能出現任何類的成員(包括變數和函式);2、由於使用了static 成員,類在被繼承時受到了限制。
(2) 使用乙個函式引數含有物件資訊的static 型別的成員函式為中轉間接地呼叫其他成員函式,以例3為例,將類test3作如下修改(黑體字為修改之處),main()函式不變,則可順利通過編譯:
class test3
void memberfun1( void (* f2)(test3*)) //將物件資訊傳給helper函式。
void memberfun2( ) //成員函式2。
void memberfun3( )
//…};
這種間接方式對成員函式沒有任何限制,克服了第一種方法成員函式不能使用任何類的成員的缺點,但由於有static 成員,類的繼承仍受到制約。
(3)使用乙個全程函式(global function)為中轉間接呼叫類的成員函式,仍以例3為例,將**作如下修改(vc++6.0編譯通過):
class test3;
void __cdecl helper(test3* test3);
class test3
//成員函式1呼叫成員函式//2。
void memberfun2( ) //成員函式2。
void memberfun3( )
//…};
void __cdecl helper(test3* test3)
; 這個方法對成員函式沒有任何要求,但是需要較多的**。
除上述三種方法外還有其他方法,如, 可以在彙編層面上修改**解決上述問題等,不屬於本文範圍。
結論:函式指標不能直接呼叫類的成員函式,需採取間接的方法,原因是成員函式指標與一般函式指標有根本的不同,成員函式指標除包含位址資訊外,同時攜帶其所屬物件資訊。本文提供三種辦法用於間接呼叫成員函式。這三種辦法各有優缺點,適用於不同的場合。
類物件的成員函式有時可以當作一般的 ansi c 函式一樣處理。正如可以宣告指向物件資料成員的一般指標一樣,也可以宣告指向物件成員函式的指標,然後通過該指標間接呼叫該函式。請看例1:
例1 使用成員函式指標class example
long get_time()
};int main()
這種表示法有點討厭。也許這就是一些程式設計師避免使用它的原因。指標 fp 用來呼叫類 example 的兩個不同的函式。同一指標可以呼叫不同的成員函式,但這些成員函式必須接收同樣數目和型別的引數,返回同樣的型別的值。如果成員函式需要引數,這種指標宣告方法有些變化,如下面的例2:
例2 指向帶引數的成員函式指標#include
class example
long set_name(char* str)
};int main()
上述**給出了三種通過指標呼叫函式的方法。還有乙個更大的變化:函式呼叫可以與物件指標連用。下面是類 example 的例子:
int main()
不管傳值還是傳位址方式,呼叫非靜態成員函式時都必須與具體的類的物件聯絡起來。靜態成員函式有所不同,宣告靜態成員函式指標的 方法也有不同。不能用物件或物件的指標呼叫靜態成員。如下面例3:
例3 指向靜態成員函式的指標class staticexample
;int value; //定義乙個全域性變數
int staticexample::foo()
int staticexample::foo()
int main()
參考<*pfun)(x, y);}
struct cpoint3d : public cpoint};
int main(int argc, char* argv)
void (cpoint::*pfun)(double x_, double y_);這裡是"指向類成員函式的指標"的宣告,就是多了cpoint::的限定
pfun = &cpoint::plus;
這裡是"指向類成員函式的指標"的賦值,在賦值的時候必須用這種靜態的方式
(pt.*pfun)(10, 10);
這裡是"指向類成員函式的指標"的使用,記住,解引用必須有實際的this指標位址,因此必須用有位址的物件pt來解引用,.*的語法有些怪異,不過我寧願把它拆解為pt.和*pfun兩部分來理解
offset = (int&)pfun;
這裡offset=4198410,當然不同的專案,不同的編譯器這個值是不同的,由此也可以知道,"指向類成員函式的指標"確實是乙個指標,其實由c++物件模型我們就應該知道這個結論了
,在c++物件模型中,成員函式是全域性的,並不屬於物件
有人想用這個值嗎?或許可以用下面的**:
void (cpoint::*pfun2)(double x_, double y_);
memcpy(&pfun2, &offset, sizeof(int));
oper(&pt, pfun2, 10, 10);
不過,我還是忍不住奉勸各位,盡量不要直接使用這個值,這畢竟是編譯器內部實現的細節,實在有太多的人喜歡這種黑客似的**並四處炫耀,真正的"指向類成員函式的指標"
的用法只應該包括宣告,賦值和解引用
pfun = &cpoint::move;注意到這裡的move是虛函式,那麼這裡還支援虛函式的多型嗎?沒有問題,"指向類成員函式的指標"支援多型,當然了,代價是,這時候這個指標就必須擴充套件為乙個結構了,c++為了
"指向類成員函式的指標"支援多型是要付出代價的
p3dfun = pfun; //正確存在基類的"指向類成員函式的指標"到派生類的"指向類成員函式的指標"的隱式轉換,其含義無疑是說基類的成員函式布局資訊只是派生類中成員函式布局資訊的乙個子集,
因此這樣的轉換應該是沒有問題的,但是反過來呢?
//pfun = p3dfun; //錯誤
不存在派生類的"指向類成員函式的指標"到基類的"指向類成員函式的指標"的隱式轉換,因為派生類中的成員函式並不一定能夠在基類中找到
"指向類成員函式的指標"基類和派生類的關係和"指向類物件的指標"基類和派生類的關係完全相反,就"指向類成員函式的指標"的本質來說,這是合理的,但是這樣的話,
我們就無法利用公共的oper函式了,除非...
pfun = (void (cpoint::*)(double, double))p3dfun; //強制轉換我們做強制轉換是可以的
oper(&pt3d, (void (cpoint::*)(double, double))p3dfun, 10, 10);
而且也只有強制轉換才可以利用公共的oper函式了,這裡的oper呼叫的是pt3d中的move函式,沒有錯誤的
但是是否一定要這樣做呢?這取決於程式設計師自己的選擇
指向類資料成員 類成員函式的指標
成員指標是指可以指向類的非靜態成員的指標,一般情況下指標指向乙個物件,但是成員指標指示的是類的成員,而非類的物件,類的靜態成員不屬於任何物件,因此無須特殊的指向靜態成員的指標,指向靜態成員的指標與普通指標沒有什麼區別,成員指標的型別囊括了類的型別以及成員的型別,當初始化乙個這樣的指標時,我們令其指向...
指向類成員的指標
一 指向類的普通成員的指標 非靜態 1 指向類成員函式的指標 簡單的講,指向類成員函式的指標與普通函式指標的區別在於,前者不僅要匹配函式的引數型別和個數以及返回值型別,還要匹配該函式指標所屬的類型別。總結一下,比較以下幾點 a 引數型別和個數 b 返回值型別 c 所屬的類型別 特別之處 究其原因,是...
指向類成員的指標
前面曾寫過一篇惱人的函式指標 一 總結了普通函式指標的宣告 定義以及呼叫,還有函式指標陣列,函式指標用作返回值等。但是作為c 的研讀,我發現我漏掉了乙個最重要的內容,就是指向類成員的指標,這裡將做相應補充 相關 測試環境為vs 2010 指向類成員的指標總的來講可以分為兩大類四小類 指向資料成員還是...