日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Boost】boost库中function的用法

發布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Boost】boost库中function的用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

要開始使用 Boost.Function, 就要包含頭文件?"boost/function.hpp", 或者某個帶數字的版本,從"boost/function/function0.hpp"?到?"boost/function/function10.hpp". 如果你知道你想保存在?function?中的函數的參數數量,這樣做可以讓編譯器僅包含需要的頭文件。如果包含?"boost/function.hpp", 那么就會把其它的頭文件也包含進去。

理解被存函數的最佳方法是把它想象為一個普通的函數對象,該函數對象用于封裝另一個函數(或函數對象)。這個被存的函數的最大用途是它可以被多次調用,而無須在創建?function?時立即使用。在聲明?functions 時,聲明中最重要的部分是函數的簽名。這部分即是告訴?function?它將保存的函數或函數對象的簽名和返回類型。我們已經看到,有兩種方法來執行這個聲明。這里有一個完整的程序,程序聲明了一個?boost::function?,它可以保存返回bool?(或某個可以隱式轉換為?bool?的類型)并接受兩個參數的類函數實體,第一個參數可以轉換為?int, 第二個參數可以轉換為?double.

[cpp] view plain copy
  • #include?<iostream>??
  • #include?"boost/function.hpp"??
  • bool?some_func(int?i,double?d)?{??
  • ??return?i>d;??
  • }??
  • int?main()?{??
  • ??boost::function<bool?(int,double)>?f;??
  • ??f=&some_func;??
  • ??f(10,1.1);??
  • }??
  • 當?function f?首次創建時,它不保存任何函數。它是空的,可以在一個布爾上下文中進行測試。如果你試圖調用一個沒有保存任何函數或函數對象的?function?,它將拋出一個類型?bad_function_call?的異常。為了避免這個問題,我們用普通的賦值語法把一個指向?some_func?的指針賦值給?f?。這導致?f?保存了到?some_func?的指針。最后,我們用參數10 (一個?int) 和 1.1 (一個?double)來調用?f?(用函數調用操作符)。要調用一個?function, 你必須提供被存函數或函數對象所期望的準確數量的參數。

    回調的基礎

    我們先來看看在沒有 Boost.Function 以前我們如何實現一個簡單的回調,然后再把代碼改為使用?function, 并看看會帶來什么優勢。我們從一個支持某種簡單的回調形式的類開始,它可以向任何對新值關注的對象報告值的改變。這里的回調是一種傳統的C風格回調,即使用普通函數。這種回調用可用于象GUI控制這樣的場合,它可以通知觀察者用戶改變了它的值,而不需要對監聽該信息的客戶有任何特殊的知識。

    [cpp] view plaincopy
  • #include?<iostream>??
  • #include?<vector>??
  • #include?<algorithm>??
  • #include?"boost/function.hpp"??
  • void?print_new_value(int?i)?{??
  • ??std::cout?<<???
  • ????"The?value?has?been?updated?and?is?now?"?<<?i?<<?'/n';??
  • }??
  • void?interested_in_the_change(int?i)?{??
  • ??std::cout?<<?"Ah,?the?value?has?changed./n";??
  • }??
  • class?notifier?{??
  • ??typedef?void?(*function_type)(int);??
  • ??std::vector<function_type>?vec_;??
  • ??int?value_;??
  • public:??
  • ??void?add_observer(function_type?t)?{??
  • ????vec_.push_back(t);??
  • ??}??
  • ??void?change_value(int?i)?{??
  • ????value_=i;??
  • ????for?(std::size_t?i=0;i<vec_.size();++i)?{??
  • ??????(*vec_[i])(value_);??
  • ????}??
  • ??}??
  • };??
  • int?main()?{??
  • ??notifier?n;??
  • ??n.add_observer(&print_new_value);??
  • ??n.add_observer(&interested_in_the_change);??
  • ??
  • ??n.change_value(42);??
  • }??
  • 這里的兩個函數,print_new_value?和?interested_in_the_change, 它們的函數簽名都兼容于?notifier?類的要求。這些函數指針被保存在一個?vector?內,并且無論何時它的值被改變,這些函數都會在一個循環里被調用。調用這些函數的一種語法是:

    (*vec_[i])(value_);

    值(value_)被傳遞給解引用的函數指針(即?vec_[i]?所返回的)。另一種寫法也是有效的,即這樣:

    vec_[i](value_);

    這種寫法看起來更好看些,但更為重要的是,它還可以允許你把函數指針更換為 Boost.Function 而沒有改變調用的語法。現在,工作還是正常的,但是,唉,函數對象不能用于這個?notifier?類。事實上,除了函數指針以外,別的任何東西都不能用,這的確是一種局限。但是,如果我們使用 Boost.Function,它就可以工作。重寫這個?notifier類非常容易。

    [cpp] view plaincopy
  • class?notifier?{??
  • ??typedef?boost::function<void(int)>?function_type;??
  • ??std::vector<function_type>?vec_;??
  • ??int?value_;??
  • public:??
  • ??template?<typename?T>?void?add_observer(T?t)?{??
  • ????vec_.push_back(function_type(t));??
  • ??}??
  • ??
  • ??void?change_value(int?i)?{??
  • ????value_=i;??
  • ????for?(std::size_t?i=0;i<vec_.size();++i)?{??
  • ??????vec_[i](value_);??
  • ????}??
  • ??}??
  • };??
  • 首先要做的事是,把?typedef?改為代表?boost::function?而不是函數指針。之前,我們定義的是一個函數指針;現在,我們使用泛型方法,很快就會看到它的用途。接著,我們把成員函數?add_observer?的簽名改為泛化的參數類型。我們也可以把它改為接受一個?boost::function,但那樣會要求該類的用戶必須也知道?function?的使用方法[2],而不是僅僅知道這個觀察者類型的要求就行了。應該注意到?add_observer?的這種變化并不應該是轉向function?的結果;無論如何代碼應該可以繼續工作。我們把它改為泛型的;現在,不管是函數指針、函數對象,還是?boost::function?實例都可以被傳遞給?add_observer, 而無須對已有用戶代碼進行任何改動。把元素加入到vector?的代碼有一些修改,現在需要創建一個?boost::function<void(int)>?實例。最后,我們把調用這些函數的語法改為可以使用函數、函數對象以及?boost::function?實例[3]。這種對不同類型的類似函數的"東西"的擴展支持可以立即用于帶狀態的函數對象,它們可以實現一些用函數很難做到的事情。

    [2]?他們應該知道 Boost.Function,但如果他們不知道呢?我們添加到接口上的任何東西都必須及時向用戶解釋清楚。

    [3]?現在我們知道,一開始我們就應該用這種語法。

    [cpp] view plaincopy
  • class?knows_the_previous_value?{??
  • ??int?last_value_;??
  • public:??
  • ??void?operator()(int?i)?{??
  • ????static?bool?first_time=true;??
  • ????if?(first_time)?{??
  • ??????last_value_=i;??
  • ??????std::cout?<<???
  • ????????"This?is?the?first?change?of?value,?/??
  • so?I?don't?know?the?previous?one./n";??
  • ??????first_time=false;??
  • ??????return;??
  • ????}??
  • ????std::cout?<<?"Previous?value?was?"?<<?last_value_?<<?'/n';??
  • ????last_value_=i;??
  • ??}??
  • };??
  • 這個函數對象保存以前的值,并在值被改變時把舊值輸出到?std::cout?。注意,當它第一次被調用時,它并不知道舊值。這個函數對象在函數中使用一個靜態?bool?變量來檢查這一點,該變量被初始化為?true. 由于函數中的靜態變量是在函數第一次被調用時進行初始化的,所以它僅在第一次調用時被設為?true?。雖然也可以在普通函數中使用靜態變量來提供狀態,但是我們必須知道那樣不太好,而且很難做到多線程安全。因此,帶狀態的函數對象總是優于帶靜態變量的普通函數。notifier?類并不關心這是不是函數對象,只要符合要求就可以接受。以下更新的例子示范了它如何使用。

    [cpp] view plaincopy
  • int?main()?{??
  • ??notifier?n;??
  • ??n.add_observer(&print_new_value);??
  • ??n.add_observer(&interested_in_the_change);??
  • ??n.add_observer(knows_the_previous_value());??
  • ??
  • ??n.change_value(42);??
  • ??std::cout?<<?'/n';??
  • ??n.change_value(30);??
  • }??
  • 關鍵一點要注意的是,我們新增的一個觀察者不是函數指針,而是一個?knows_the_previous_value?函數對象的實例。運行這段程序的輸出如下:

    The value has been updated and is now 42 Ah, the value has changed. This is the first change of value, so I don't know the previous one.The value has been updated and is now 30 Ah, the value has changed. Previous value was 42

    在這里最大的優點不是放寬了對函數的要求(或者說,增加了對函數對象的支持),而是我們可以使用帶狀態的對象,這是非常需要的。我們對?notifier?類所做的修改非常簡單,而且用戶代碼不受影響。如上所示,把 Boost.Function 引入一個已有的設計中是非常容易的。

    類成員函數

    Boost.Function 不支持參數綁定,這在每次調用一個?function?就要調用同一個類實例的成員函數時是需要的。幸運的是,如果這個類實例被傳遞給?function?的話,我們就可以直接調用它的成員函數。這個?function?的簽名必須包含類的類型以及成員函數的簽名。換言之,顯式傳入的類實例要作為隱式的第一個參數,this。這樣就得到了一個在給出的對象上調用成員函數的函數對象。看一下以下這個類:

    [cpp] view plaincopy
  • class?some_class?{??
  • public:??
  • ??void?do_stuff(int?i)?const?{??
  • ????std::cout?<<?"OK.?Stuff?is?done.?"?<<?i?<<?'/n';??
  • ??}??
  • };??
  • 成員函數?do_stuff?要從一個?boost::function?實例里被調用。要做到這一點,我們需要 function 接受一個some_class?實例,簽名的其它部分為一個?void?返回以及一個?int?參數。對于如何把?some_class?實例傳給 function,我們有三種選擇:傳值,傳引用,或者傳址。如何要傳值,代碼就應該這樣寫[4]

    [4]?很少會有理由來以傳值的方式傳遞對象參數。

    [cpp] view plaincopy
  • boost::function<void(some_class,int)>?f;??
  • 注意,返回類型仍舊在最開始,后跟成員函數所在的類,最后是成員函數的參數類型。它就象傳遞一個?this?給一個函數,該函數暗地里用類實例調用一個非成員函數。要把函數?f?配置為成員函數?do_stuff, 然后調用它,我們這樣寫:

    [cpp] view plaincopy
  • f=&some_class::do_stuff;??
  • f(some_class(),2);??
  • 如果要傳引用,我們要改一下函數的簽名,并傳遞一個?some_class?實例。

    [cpp] view plaincopy
  • boost::function<void(some_class&,int)>?f;??
  • f=&some_class::do_stuff;??
  • some_class?s;??
  • f(s,1);??
  • 最后,如果要傳?some_class?的指針[5],我們就要這樣寫:

    [5]?裸指針或智能指針皆可。

    [cpp]?view plaincopy
  • boost::function<void(some_class*,int)>?f;??
  • f=&some_class::do_stuff;??
  • some_class?s;??
  • f(&s,3);??
  • 好了,所有這些傳遞"虛擬?this"實例的方法都已經在庫中提供。當然,這種技術也是有限制的:你必須顯式地傳遞類實例;而理想上,你更愿意這個實例被綁定在函數中。乍一看,這似乎是 Boost.Function 的缺點,但有別的庫可以支持參數的綁定,如 Boost.Bind 和 Boost.Lambda. 我們將在本章稍后的地方示范這些庫會給 Boost.Function 帶有什么好處。

    帶狀態的函數對象

    我們已經看到,由于支持了函數對象,就可以給回調函數增加狀態。考慮這樣一個類,keeping_state, 它是一個帶狀態的函數對象。keeping_state?的實例記錄一個總和,它在每次調用操作符執行時被增加。現在,將該類的一個實例用于兩個?boost::function?實例,結果有些出人意外。

    [cpp] view plaincopy
  • #include?<iostream>??
  • #include?"boost/function.hpp"??
  • class?keeping_state?{??
  • ??int?total_;??
  • public:??
  • ??keeping_state():total_(0)?{}??
  • ??int?operator()(int?i)?{??
  • ????total_+=i;??
  • ????return?total_;??
  • ??}??
  • ??int?total()?const?{??
  • ????return?total_;??
  • ??}??
  • };??
  • int?main()?{??
  • ??keeping_state?ks;??
  • ??boost::function<int(int)>?f1;??
  • ??f1=ks;??
  • ??boost::function<int(int)>?f2;??
  • ??f2=ks;??
  • ??std::cout?<<?"The?current?total?is?"?<<?f1(10)?<<?'/n';??
  • ??std::cout?<<?"The?current?total?is?"?<<?f2(10)?<<?'/n';??
  • ??std::cout?<<?"After?adding?10?two?times,?the?total?is?"???
  • ????<<?ks.total()?<<?'/n';??
  • }??
  • 寫完這段代碼并接著執行它,程序員可能期望保存在?ks?的總和是20,但不是;事實上,總和為0。以下是這段程序的運行結果。

    The current total is 10 The current total is 10 After adding 10 two times, the total is 0

    原因是每一個?function?實例(f1?和?f2)都含有一個?ks?的拷貝,這兩個實例得到的總和都是10,但?ks?沒有變化。這可能是也可能不是你想要的,但是記住,boost::function?的缺省行為是復制它要調用的函數對象,這一點很重要。如果這導致不正確的語義,或者如果某些函數對象的復制代價太高,你就必須把函數對象包裝在boost::reference_wrapper?中,那樣?boost::function?的復制就會是一個?boost::reference_wrapper?的拷貝,它恰好持有一個到原始函數對象的引用。你無須直接使用?boost::reference_wrapper?,你可以使用另兩個助手函數,ref?和?cref。 這兩函數返回一個持有到某特定類型的引用或?const?引用的?reference_wrapper。在前例中,要獲得我們想要的語義,即使用同一個?keeping_state?實例,我們就需要把代碼修改如下:

    [cpp] view plaincopy
  • int?main()?{??
  • ??keeping_state?ks;??
  • ??boost::function<int(int)>?f1;??
  • ??f1=boost::ref(ks);??
  • ??
  • ??boost::function<int(int)>?f2;??
  • ??f2=boost::ref(ks);??
  • ??
  • ??std::cout?<<?"The?current?total?is?"?<<?f1(10)?<<?'/n';??
  • ??std::cout?<<?"The?current?total?is?"?<<?f2(10)?<<?'/n';??
  • ??std::cout?<<?"After?adding?10?two?times,?the?total?is?"???
  • ????<<?ks.total()?<<?'/n';??
  • }??
  • boost::ref?的用途是通知?boost::function,我們想保存一個到函數對象的引用,而不是一個拷貝。運行這個程序有以下輸出:

    The current total is 10 The current total is 20 After adding 10 two times, the total is 20

    這正是我們想要的結果。使用?boost::ref?和?boost::cref?的不同之處就象引用與?const?引用的差異,對于后者,你只能調用其中的常量成員函數。以下例子使用一個名為?something_else?的函數對象,它有一個?const?的調用操作符。

    [cpp] view plaincopy
  • class?something_else?{??
  • public:??
  • ??void?operator()()?const?{??
  • ????std::cout?<<?"This?works?with?boost::cref/n";??
  • ??}??
  • };??
  • 對于這個函數對象,我們可以使用?boost::ref?或?boost::cref.

    [cpp] view plaincopy
  • something_else?s;??
  • boost::function0<void>?f1;??
  • f1=boost::ref(s);??
  • f1();??
  • boost::function0<void>?f2;??
  • f2=boost::cref(s);??
  • f2();??
  • 如果我們改變了?something_else?的實現,使其函數為非const, 則只有?boost::ref?可以使用,而?boost::cref?將導致一個編譯期錯誤。

    [cpp] view plaincopy
  • class?something_else?{??
  • public:??
  • ??void?operator()()?{??
  • ????std::cout?<<???
  • ??????"This?works?only?with?boost::ref,?or?copies/n";??
  • ??}??
  • };??
  • ??
  • something_else?s;??
  • boost::function0<void>?f1;??
  • f1=boost::ref(s);?//?This?still?works??
  • f1();???
  • boost::function0<void>?f2;??
  • f2=boost::cref(s);?//?This?doesn't?work;???
  • ???????????????????//?the?function?call?operator?is?not?const??
  • f2();??
  • 如果一個?function?包含一個被?boost::reference_wrapper?所包裝的函數對象,那么復制構造函數與賦值操作就會復制該引用,即?function?的拷貝將引向原先的函數對象。

    [cpp] view plaincopy
  • int?main()?{??
  • ??keeping_state?ks;??
  • ??boost::function1<int,int>?f1;??//?譯注:原文為boost::function<int,int>?f1,有誤??
  • ??f1=boost::ref(ks);??
  • ??
  • ??boost::function1<int,int>?f2(f1);??//?譯注:原文為boost::function<int,int>?f2(f1),有誤???
  • ??boost::function1<short,short>?f3;??//?譯注:原文為boost::function<short,short>?f3,有誤???
  • ??f3=f1;??
  • ??
  • ??std::cout?<<?"The?current?total?is?"?<<?f1(10)?<<?'/n';??
  • ??std::cout?<<?"The?current?total?is?"?<<?f2(10)?<<?'/n';??
  • ??std::cout?<<?"The?current?total?is?"?<<?f3(10)?<<?'/n';??
  • ??std::cout?<<?"After?adding?10?three?times,?the?total?is?"???
  • ????<<?ks.total()?<<?'/n';??
  • }??
  • 這等同于使用?boost::ref?并把函數對象?ks?賦給每一個 function 實例。

    給回調函數增加狀態,可以發揮巨大的能力,這也正是使用 Boost.Function 與使用函數對象相比具有的非常突出的優點。


    總結

    以上是生活随笔為你收集整理的【Boost】boost库中function的用法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。