C++回調(diào)函數(shù)(callback)與仿函數(shù)(functor)的異同
許式偉 (版權(quán)聲明)
2007-3-3
回調(diào)函數(shù)(callback)與仿函數(shù)(functor)很多時候從用途上來看很相似,以致于我們經(jīng)常將它們相提并論。例如:
inline bool compare(int a, int b)
{
return a > b;
}
struct comparer {
bool operator()(int a, int b) const {
return a > b;
}
};
void main()
{
std::vector<int> vec, vec2;
std::sort(vec.begin(), vec.end(), compare);
std::sort(vec2.begin(), vec2.end(), comparer());
}
仿函數(shù)(functor)之所以稱為仿函數(shù),是因為這是一種利用某些類對象支持operator()的特性,來達(dá)到模擬函數(shù)調(diào)用效果的技術(shù)。
如果這里vec, vec2這兩個vector的內(nèi)容一樣,那么從執(zhí)行結(jié)果看,使用回調(diào)函數(shù)compare與使用仿函數(shù)comparer是一樣的。
那么,我們應(yīng)該用回調(diào),還是用仿函數(shù)?
很多人都說用仿函數(shù)吧,回調(diào)函數(shù)是丑陋的,代碼不太象C++風(fēng)格。
但其實問題的本質(zhì)不是在代碼風(fēng)格上,仿函數(shù)與回調(diào)函數(shù)各有利弊,不能一概而論。
仿函數(shù)(functor)的優(yōu)點
我的建議是,如果可以用仿函數(shù)實現(xiàn),那么你應(yīng)該用仿函數(shù),而不要用回調(diào)。原因在于:
- 仿函數(shù)可以不帶痕跡地傳遞上下文參數(shù)。而回調(diào)技術(shù)通常使用一個額外的void*參數(shù)傳遞。這也是多數(shù)人認(rèn)為回調(diào)技術(shù)丑陋的原因。
- 更好的性能。
仿函數(shù)技術(shù)可以獲得更好的性能,這點直觀來講比較難以理解。你可能說,回調(diào)函數(shù)申明為inline了,怎么會性能比仿函數(shù)差?我們這里來分析下。我們假設(shè)某個函數(shù)func(例如上面的std::sort)調(diào)用中傳遞了一個回調(diào)函數(shù)(如上面的compare),那么可以分為兩種情況:
- func是內(nèi)聯(lián)函數(shù),并且比較簡單,func調(diào)用最終被展開了,那么其中對回調(diào)函數(shù)的調(diào)用也成為一普通函數(shù)調(diào)用(而不是通過函數(shù)指針的間接調(diào)用),并且如果這個回調(diào)函數(shù)如果簡單,那么也可能同時被展開。在這種情形下,回調(diào)函數(shù)與仿函數(shù)性能相同。
- func是非內(nèi)聯(lián)函數(shù),或者比較復(fù)雜而無法展開(例如上面的std::sort,我們知道它是快速排序,函數(shù)因為存在遞歸而無法展開)。此時回調(diào)函數(shù)作為一個函數(shù)指針傳入,其代碼亦無法展開。而仿函數(shù)則不同。雖然func本身復(fù)雜不能展開,但是func函數(shù)中對仿函數(shù)的調(diào)用是編譯器編譯期間就可以確定并進(jìn)行inline展開的。因此在這種情形下,仿函數(shù)比之于回調(diào)函數(shù),有著更好的性能。并且,這種性能優(yōu)勢有時是一種無可比擬的優(yōu)勢(對于std::sort就是如此,因為元素比較的次數(shù)非常巨大,是否可以進(jìn)行內(nèi)聯(lián)展開導(dǎo)致了一種雪崩效應(yīng))。
仿函數(shù)(functor)不能做的?
話又說回來了,仿函數(shù)并不能完全取代回調(diào)函數(shù)所有的應(yīng)用場合。例如,我在std::AutoFreeAlloc中使用了回調(diào)函數(shù),而不是仿函數(shù),這是因為AutoFreeAlloc要容納異質(zhì)的析構(gòu)函數(shù),而不是只支持某一種類的析構(gòu)。這和模板(template)不能處理在同一個容器中支持異質(zhì)類型,是一個道理。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1519828