C 高效定義STL比較函式的一些建議

2021-07-15 03:52:38 字數 4455 閱讀 8458

在stl的使用中,我們經常需要自定義比較函式。本文將介紹如何完成這一類的函式,並且給出可靠而高效的使用建議。

mem_fun, ptr_fun, mem_fun_ref主要的任務是為了掩蓋c++語言中乙個內在的語法不一致的問題。

呼叫乙個函式,c++提供了三種方法。

f(x); //  語法1:非成員函式的呼叫。

x.f(); // 語法2:成員函式的呼叫。

p->f(); // 語法3:指標呼叫成員函式。

對於語法1:

#include 

#include

using

namespace

std;

class widget ;

void test(const widget& one)

int main()

對於語法2:上面的寫法就不再合適了,後文給出相應解釋。正確的做法如下,呼叫mem_fun_ref。

#include 

#include

using

namespace

std;

class widget

};int main()

對於語法3:

#include 

#include

using

namespace

std;

class widget

};int main()

這三種不同情況的呼叫寫法。那麼原因究竟是什麼呢?

從以下for_each的實現中,我們可以看出,for_each的實現是基於使用語法1的

unaryfunction for_each(inputit first, inputit last, unaryfunction f)

return f;

}這是stl中乙個常見的慣例,函式或者函式物件在被呼叫時,總是使用非成員函式的語法形式。 所以直接使用語法2和語法3無法通過編譯也就非常顯然了。而mem_fun和mem_fun_ref就是為了把他們轉換為相應的語法1的形式。

mem_fun的宣告是這樣的:

template c>

mem_fun_tc>

mem_fun(r(c::*pmf)());

mem_fun_t是乙個函式物件配接器,他是乙個函式子類,他擁有該函式物件的指標,並且提供了operator()函式,在 operator()中呼叫通過引數傳遞進來的物件上的該成員函式。

與此類似的,mem_fun_ref也做了相同的事情。

所以簡單的說,每次在講乙個成員函式傳遞給乙個stl元件的時候,就要使用它們。

在stl中4個標準的函式配接器(not1, not2, bind1st和bind2nd)都要求一些特殊的型別定義(分別是argument_type, first_argument_type, second_argument_type, return_type)。提供了這些型別定義的函式物件被稱為可配接的函式物件。

#include 

#include

using

namespace

std;

bool check(int i)

int main() ;

vector

::iterator iter = find_if(v_i.begin(), v_i.end(), not1(check));

cout

<< *iter << endl;

return

0;}

這個**的意圖是找到第乙個不滿足check的元素。但發現這個函式是不能通過編譯的。原因在與not1需要特殊的型別定義,所以需要做乙個簡單的調整才能通過編譯。

vector::iterator iter = find_if(v_i.begin(), v_i.end(), not1(ptr_fun(check)));
如果你需要編寫函式子類的話,一定要從基結構中繼承。operator()只有乙個引數時,繼承std::unary_function, 有兩個引數時,繼承std::binary_function。

#include 

#include

using

namespace

std;

class check : public unary_function

bool

operator()(const check& orig) const

};int main() ;

vector

::iterator iter = find_if(v_i.begin(), v_i.end(), not1(check(1)));

cout

<< *iter << endl;

return

0;}

如果此時沒有繼承於 unary_function

#include 

#include

using

namespace

std;

struct check : public binary_functionint*, const

int*, bool>

};int main()

c++和c的標準庫函式都遵循乙個規則,函式指標都是按值傳遞的。

stl函式物件是函式指標的一種抽象和建模形式,所以,按照慣例,在stl中,函式物件在函式之間來回傳遞的時候也是按值傳遞的。這雖然可以通過強制型別來使其按引用傳遞,但這種做法是很危險的。因為stl的某些配接器和演算法在接受函式物件時,會考慮效率,從而使其為引用型別,如果此時你在模板引數中再宣告為引用,引用的引用是不可編譯的。

函式指標是按值傳遞意味著兩件事:

但是檢視禁止多型的函式子同樣是不切實際的,解決方法就是把所需的資料和虛函式從函式子類中分離出來,放在乙個新的類中,然後在函式子類中包含乙個指標,指向這個新類的物件。

例如,你希望建立乙個包含大量資料並且使用了多型性的函式子類:

template

class bpfc : unary_functionvoid> ;

那麼就應該建立乙個小巧的,單態的類,其中包含乙個指標,指向另乙個實現的類,並且所有的資料和虛函式都放在哪個實現類中:

template

class bpfc : unary_functionvoid>

};template

class bpfcimpl : unary_functionvoid> ;

這裡還有最後乙個問題就是,要謹慎處理bpfc的拷貝建構函式,使其正確地處理它所指向的bpfcimpl物件。簡單的方法是使用引用計數的智慧型指標。shard_ptr。

首先給出幾個概念。

那麼為什麼要確保判別式是純函式呢?

前文我們提到,函式物件是通過傳值傳遞的,所以你應該設計出可以被正確複製的函式物件。除此以外,對於用作判別式的函式物件,當它們被複製時,還有另乙個需要特別注意的地方:接受函式子的stl演算法可能會先建立函式子的副本,然後存放起來再使用這些副本,而且有些stl演算法實現也確實利用了這一特性,而這一特性的直接反應是:要求判別式函式一定是純函式

加入我們設計了如下的判別式類,在呼叫remove_if就會出現問題。

class badpredicate : public unary_functionbool> 

bool

operator()(const widget&)

private:

int times;

};

我們來看看remove_if的函式實現就明白了。它的可能實現是這樣的。

forwardit remove_if(forwardit first, forwardit last, unarypredicate p)

else }

本來我們是希望移除第3個元素,但通過這個實現的remove_if實際上我們還刪除了第6個元素。因為predicate被按值傳遞,函式子類被重新構造。

實際上,解決方法最簡單的就是,永遠把operator()宣告為const。但這還不夠,因為即使是const成員函式,還是可以訪問mutable, 非const的區域性static物件,非const的類static物件,非const的全域性物件等等。

上面的例子,就算是嘗試使用static變數也是不行的。因為判別式一定要是純函式!

所以,最重要的一點就是!確保判別式一定是純函式!

一些比較精妙的巨集定義

今天在網上突然發現了下面幾個關於c 中的巨集定義的說明,回想下,好像在系統的 中也見過這些零散的定義,但沒有注意,看到別人總結了下,發現果然很有用,雖然不知有的道可用與否,但也不失為一種手段,所以就先把它摘抄下來,增加一點見識 1,防止乙個標頭檔案被重複包含 ifndef bodydef h def...

C 與C 一些比較

1.在c 中不需要區分標頭檔案與實現檔案,c 為了和其他cpp中的函式方便的做互動不得不寫乙個標頭檔案來做型別宣告。2.c 語言本身提供了反射機制支援 而c 並不支援 像c 中propertygrid這樣的東西,在c 中實現也是乙個比較麻煩的事情 rpc這樣的東西在有反射系統的情況下,實現和使用都要...

一些比較重要的函式

void start void launchprojectile 協同程式 同步執行 執行結果 starting 0.0 和 before waitandprint finishes 0.0 兩句,2秒後列印 waitandprint 2.0 waitandprint在start函式內執行,可以視同...