C vs C 之一 委託 vs 函式指標

2021-06-07 20:54:53 字數 2891 閱讀 7617

委託與函式指標辨析

我們常見到c#技術文獻用「類似c/c++函式指標的東西」介紹委託。這樣好像是有道理的,因為二者的確有深層次的相通之處。委託和函式指標都描述了方法/函式的簽名,並通過統一的介面呼叫不同的實現。但二者又有明顯的區別,簡單說來,委託物件是真正的物件,而函式指標變數只是函式的入口位址。對於高層應用來講,委託

的靈活性和適用範圍

更勝函式指標;但對於底層應用,函式指標則是不可替代的。下面分別是委託型別和函式指標型別定義的例子:

delegate int fn(int a, int b) //c#委託

typedef int (*fn)(int a, int b) //c++函式指標

從形式上看,二者的引數列表和返回值都是相同的,只是乙個採用關鍵字delegate,乙個採用指標符號*。似乎「相似」的說法更有把握了,但如果馬上給二者劃等號就操之過急了。我們先實際驗證一下,看看到底有什麼不同: 

//c#

delegate int fn(int a, int b) ;

class adder

public adder(int c)

}class multiplier

public multiplier(int c)

}adder adder = new adder(1);

multiplier multiplier = new multiplier(2);

fn fn = adder.add;

fn(1, 2); //結果為4

fn = multiplier.multiple;

fn(2, 3); //結果為12

從上面的**說明了兩個問題:

1.委託物件可以指向不同類的方法,只要符合委託簽名;

2.委託物件是有狀態的(儲存在指向的物件中),委託的行為不僅受到輸入引數的影響,還受到目標物件狀態的影響。

//c++

typedef int(*fn)(int a, int b);

int add(int a, int b) ;

int multiple(int a, int b) ;

class adder 

int add(int a, int b) 

private: 

int c; };

typedef int(adder::* fm)(int a, int b);

int _tmain(int argc, _tchar* argv) 

c#中的委託是一種支援()操作符的特殊物件。這和c/c++的函式指標是有本質區別的,因為c/c++的函式指標變數並不具有物件性質,它只是單純的函式入口位址。上面的fn只能指向add和multiple兩個普通函式,無法指向adder類的add方法。因為adder類的add方法的簽名並非int(*)(int a, int b),編譯器會自動加上乙個隱式的this指標引數,所以它的簽名是類似int(*)(adder *const this, int a, int b) 的。如果需要指向成員函式的指標,需要用typedef int(adder::* fm)(int a, int b)這樣的形式加上型別限定符。 所以,c++的函式指標不能像c#委託一樣指向不同類的方法;不具有物件的狀態性質;在使用上函式指標也不如委託靈活。所以,當聽到「委託就是類似c/c++函式指標」的說法的時候應該既理解其相似之處,又明了其差別。

functor

我們常說c++是強大而複雜的語言,函式指標已經被c#委託pk下來了,難道c++就沒有可以pk c#委託的大將嗎?當然有!首先應該看到,函式指標並非c++的產物,而是繼承自c,因此函式指標的侷限其實是c的侷限,與c++無關。我們說c#委託是支援()函式呼叫操作符的特殊物件,在c++中()操作符是可以被過載的,我們把過載了()操作符的物件稱為functor。下面是乙個functor的例子:

class adder 

int operator()(int a, int b) 

private: 

int c; };

int _tmain(int argc, _tchar* argv) 

functor和委託物件有相似之處,都是支援()操作符的具有狀態的物件。但functor還不是委託,為什麼?因為委託型別是一種介面規範,函式指標型別也是,但functor本身不是介面規範。這裡,請注意區分型別和物件之間的關係:委託型別和委託物件,函式指標型別和函式指標變數。functor可以等同於委託物件,但如何表達委託型別呢?應該看到,委託機制是一種執行時的動多型,委託物件可以與目標方法動態地繫結。由於c++並非基於虛擬機器的語言,想直接動態繫結不同型別functor到統一的型別介面並不容易。但c++有一種強大的工具實現靜多型,這就是模版:

模版class adder 

int operator()(int a, int b) 

private: 

int c; };

class multiplier 

int operator()(int a, int b) 

private: 

int c; };

template

void do(t& f, int a, int b) ;

int _tmain(int argc, _tchar* argv) 

可以認為

函式模版do表達了介面規範,它要求乙個泛型類t支援()運算子、接受兩個整形引數、返回可隱式轉換為int的型別。對於不同型別adder和multiplier,編譯器在編譯時自動根據模版為不同的型別t生成了不同的do過載函式,因此do的呼叫形式是統一的,這就是所謂的靜多型。有人談到「模版為靜態型別的c++賦予了幾乎無型別的元程式設計能力」,這個例子算是個小小的窺視。stl中許多庫方法都是通過模版來表達介面規範,呼叫者傳入functor,其靈活性完全不輸c#委託。

總結1.c#委託物件是真正的物件,c/c++函式指標只是函式入口位址

2.c++的委託物件:functor

3.c++的靜多型:模版

C vs C 之一 委託 vs 函式指標

我們常見到c 技術文獻用 類似c c 函式指標的東西 介紹委託。這樣好像是有道理的,因為二者的確有深層次的相通之處。委託和函式指標都描述了方法 函式的簽名,並通過統一的介面呼叫不同的實現。但二者又有明顯的區別,簡單說來,委託物件是真正的物件,而函式指標變數只是函式的入口位址。對於高層應用來講,委託 ...

函式指標VS指標函式 陣列指標VS指標陣列

定義乙個函式 void fuc1 int a 宣告函式指標 int fucptr int 函式指標fucptr指向函式fuc1 fucptr fuc1 呼叫函式指標 fucptr 10 int array1 10 定義乙個陣列 int arrayptr 10 定義乙個陣列指標 arrayptr ar...

C 委託 C函式指標

c 高階程式設計 裡說 net已委託的形式實現了函式指標的概念 這裡簡單比較一下兩者的區別 1 首先看看c下的 函式指標 如何宣告和定義乙個函式變數 typedef int func int x,int y 宣告 func func 定義乙個 函式變數 當已經有這樣乙個實現的函式時 int some...