首先講回調機制。回調機制,C中用函數指針,C++中用函數對象。
考慮一個比較大小的函數,當然,這個例子比較傻
C中:
int cmp(void* a,void* b,int (*fun)(const void*,const void*))
{
return fun(a,b);
}
我希望cmp可以用于任意類型的比較。第三個參數傳函數指針,這就是C語言的回調機制。真正做事的就是用fun函數。為了完成比較任務,自己寫用于具體類型比較的代碼,比如:
int f(const void* a,const void* b)
{
if(*(int*)a>*(int*)b) return 1;
else
return *(int*)a<*(int*)b?(-1):0;
}
這樣調用:
int a=6,b=3;
cmp((void*)&a,(void*)&b,f);
如此就實作了任意類型的比較,當然我們自己為任意其它類型寫比較函數。這種技巧可以在C庫函數qsort看到。原型是:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
第四個參數就是傳一個用于比較的函數指針。以實作泛化的比較。
再看C++:
template <class T,class op>
int cmp(const T& a,const T& b,op f)
{
return f(a,b);
}
C++中我們還是可以像C哪樣寫個用于回調的類似函數:
int f(int a,int b)
{
if(a>b) return 1;
else
return a<b?(-1):0;
}
這樣調用:
int a=6,b=3;
comp(a,b,f);
模闆機制會将第三個參數翻譯成函數指針。不過C++不鼓勵這樣。因為C++有自己的機制--函數對象。
函數對象是提供了operator()的一個類。它是一個類,但用法卻類型函數。
class op
{
public:
int operator() (int a,int b)
{
if(a>b) return 1;
else
return a<b?(-1):0;
}
};
op就是一個函數對象了。完成和C語言中函數f一樣的功能,即比較。
C和C++都是強類型語言。可以看出來,C和C++中為了解決不同類型(實作通用類型)的比較,都使用了一定的語言上的技巧。C++是用模闆。C語言是用void*的通用性。void*可以強制類型轉化為任意類型的指針。
上面實作是回調機制相關的。C++與C誰優誰劣呢?這讓我們進入下一個讨論主題:預設參數。
為了更加的通用,通過帶預設參數的方式就可以少寫一個參數了。
先看C。C語言本身不提供預設參數支援,要實作又需要一些技巧。還是上面例子,改一下。
int cmp(void* a,void* b,int (*fun)(const void*,const void*))
{
if(fun==NULL) fun=f;
return fun(a,b);
}
函數f由預設提供。這樣當我們是比較int型大小,就可以把第三個參數直接寫0了.這個技巧就可以實作預設參數了。很多C庫函數中可以看到這個技巧的應用,比如說
int setvbuf( FILE *stream, char *buffer, int mode, size_t size );
如果第二個參數傳NULL,就預設由系統幫我們申請和管理緩沖區了。
再看C++:
C++中一方面提供函數的預設參數,我們可以直接寫
int cmp(void* a,void* b,int (*fun)(const void*,const void*)=f)
{
return fun(a,b);
}
這樣調用int a=6,b=3;
cmp(&a,&b); //可以省略第三個參數,相比C語言,連傳NULL指針都不必了
C++對預設參數的支援比C進了一大步。關鍵還不是這裡。C++中,類模闆也可以提供預設的模闆參數。這種模闆預設參數的使用使得C++模闆變得威力無窮。但也同時是太過靈活的模闆,使C++變得難以控制。下面看個實際例子:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
template<class RanIt, class Pred> void sort(RanIt first, RanIt last, Pred pr);
STL的sort函數與C語言的qsort是一個很好的對比。sort傳二個iterator就夠了。而qsort卻要傳一大堆的參數.
sort通過預設參數減掉了用于比較的函數。再通過iterator traits技巧及模闆推演又減掉了類型參數。
qsort沒這些技巧,一個參數都不能少。這樣一比較上面我提出的問題就很明白了:通過語言技巧的完美利用,C++可以寫得相當優美,而C醜陋得多。預設參數讓C++威力無窮!還有,C語言雖然利用void*通用性實作了一定程度的泛化,但卻失去了強類型檢驗的部分好處。如果使用都不洽當使用,會使問題變得糟糕很多。而C++在泛化的同時沒有太多失去類型安全檢查。事物都有二面性。C++的模闆機制也帶來了很多問題。模闆很C++成了一問很複雜的語言。光是文法就極難駕馭。而有些C++吹捧的狂熱分子使問題再嚴重。iterator_traits,模闆的模闆參數,泛型程式設計,模闆元程式設計.....C++走向極端。
關于這些語言上的是是非非,我不再深入去研究。模闆好不好?自己在使用中去慢慢體會。
前面講了回調機制,預設參數,下面我來談第三個問題,最後一個問題,泛化技巧的實作本質。
C在泛化上的本質,就是利用void*可以向任意類型轉換。C++在泛化的本質上,就是模闆機制。C++的模闆,會為所有類型都生成一個函數。相當于把用于一種類型比較的代碼Ctrl+c Ctrl+v了一遍。當然,編譯器在上述過程有些優化。模闆沒有增加運作時開銷,但增加程式的代碼段的長度。相比之下C的泛化能力弱多了。void*失去了類型安全的優勢。所能做的也有限。回調機制增加了靈活性,增加了少量的運作切換開銷。不帶來其它損失。
大緻就是這些了。我剛接觸C語言,還在學習中。還是我以前說過的,多少優秀的語言也無法阻止垃圾的程式員寫出糟糕的代碼。C++我駕馭不了,現學習C語言中....