1. 可調(diào)用對(duì)象
可調(diào)用對(duì)象有一下幾種定義:
- 是一個(gè)函數(shù)指針盐须,參考 C++ 函數(shù)指針和函數(shù)類型彤蔽;
- 是一個(gè)具有operator()成員函數(shù)的類的對(duì)象偿渡;
- 可被轉(zhuǎn)換成函數(shù)指針的類對(duì)象颠放;
- 一個(gè)類成員函數(shù)指針饼酿;
C++中可調(diào)用對(duì)象的雖然都有一個(gè)比較統(tǒng)一的操作形式棉圈,但是定義方法五花八門涩堤,這樣就導(dǎo)致使用統(tǒng)一的方式保存可調(diào)用對(duì)象或者傳遞可調(diào)用對(duì)象時(shí),會(huì)十分繁瑣分瘾。C++11中提供了std::function和std::bind統(tǒng)一了可調(diào)用對(duì)象的各種操作胎围。
不同類型可能具有相同的調(diào)用形式,如:
// 普通函數(shù)
int add(int a, int b){return a+b;}
// lambda表達(dá)式
auto mod = [](int a, int b){ return a % b;}
// 函數(shù)對(duì)象類
struct divide{
int operator()(int denominator, int divisor){
return denominator/divisor;
}
};
上述三種可調(diào)用對(duì)象雖然類型不同德召,但是共享了一種調(diào)用形式:
int(int ,int)
std::function就可以將上述類型保存起來(lái)白魂,如下:
std::function<int(int ,int)> a = add;
std::function<int(int ,int)> b = mod ;
std::function<int(int ,int)> c = divide();
2. std::function
- std::function 是一個(gè)可調(diào)用對(duì)象包裝器,是一個(gè)類模板上岗,可以容納除了類成員函數(shù)指針之外的所有可調(diào)用對(duì)象福荸,它可以用統(tǒng)一的方式處理函數(shù)、函數(shù)對(duì)象肴掷、函數(shù)指針敬锐,并允許保存和延遲它們的執(zhí)行。
- 定義格式:std::function<函數(shù)類型>呆瞻。
- std::function可以取代函數(shù)指針的作用台夺,因?yàn)樗梢匝舆t函數(shù)的執(zhí)行,特別適合作為回調(diào)函數(shù)使用痴脾。它比普通函數(shù)指針更加的靈活和便利颤介。
3. std::bind
可將std::bind函數(shù)看作一個(gè)通用的函數(shù)適配器,它接受一個(gè)可調(diào)用對(duì)象,生成一個(gè)新的可調(diào)用對(duì)象來(lái)“適應(yīng)”原對(duì)象的參數(shù)列表买窟。
std::bind將可調(diào)用對(duì)象與其參數(shù)一起進(jìn)行綁定丰泊,綁定后的結(jié)果可以使用std::function保存薯定。std::bind主要有以下兩個(gè)作用:
- 將可調(diào)用對(duì)象和其參數(shù)綁定成一個(gè)防函數(shù)始绍;
- 只綁定部分參數(shù),減少可調(diào)用對(duì)象傳入的參數(shù)话侄。
3.1 std::bind綁定普通函數(shù)
double my_divide (double x, double y) {return x/y;}
auto fn_half = std::bind (my_divide,_1,2);
std::cout << fn_half(10) << '\n'; // 5
- bind的第一個(gè)參數(shù)是函數(shù)名亏推,普通函數(shù)做實(shí)參時(shí),會(huì)隱式轉(zhuǎn)換成函數(shù)指針年堆。因此std::bind (my_divide,_1,2)等價(jià)于std::bind (&my_divide,_1,2)吞杭;
- _1表示占位符,位于<functional>中变丧,std::placeholders::_1芽狗;
3.2 std::bind綁定一個(gè)成員函數(shù)
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
int main()
{
Foo foo;
auto f = std::bind(&Foo::print_sum, &foo, 95, std::placeholders::_1);
f(5); // 100
}
- bind綁定類成員函數(shù)時(shí),第一個(gè)參數(shù)表示對(duì)象的成員函數(shù)的指針痒蓬,第二個(gè)參數(shù)表示對(duì)象的地址童擎。
- 必須顯示的指定&Foo::print_sum,因?yàn)榫幾g器不會(huì)將對(duì)象的成員函數(shù)隱式轉(zhuǎn)換成函數(shù)指針攻晒,所以必須在Foo::print_sum前添加&顾复;
- 使用對(duì)象成員函數(shù)的指針時(shí),必須要知道該指針屬于哪個(gè)對(duì)象鲁捏,因此第二個(gè)參數(shù)為對(duì)象的地址 &foo芯砸;
3.3 綁定一個(gè)引用參數(shù)
默認(rèn)情況下,bind的那些不是占位符的參數(shù)被拷貝到bind返回的可調(diào)用對(duì)象中给梅。但是假丧,與lambda類似,有時(shí)對(duì)有些綁定的參數(shù)希望以引用的方式傳遞动羽,或是要綁定參數(shù)的類型無(wú)法拷貝包帚。
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;
ostream & print(ostream &os, const string& s, char c)
{
os << s << c;
return os;
}
int main()
{
vector<string> words{"helo", "world", "this", "is", "C++11"};
ostringstream os;
char c = ' ';
for_each(words.begin(), words.end(),
[&os, c](const string & s){os << s << c;} );
cout << os.str() << endl;
ostringstream os1;
// ostream不能拷貝,若希望傳遞給bind一個(gè)對(duì)象曹质,
// 而不拷貝它婴噩,就必須使用標(biāo)準(zhǔn)庫(kù)提供的ref函數(shù)
for_each(words.begin(), words.end(),
bind(print, ref(os1), _1, c));
cout << os1.str() << endl;
}
4. 指向成員函數(shù)的指針
通過(guò)下面的例子,熟悉一下指向成員函數(shù)的指針的定義方法羽德。
#include <iostream>
struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};
void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}
int main() {
Foo foo1{1};
Foo foo2{2};
apply(&foo1, &foo2, &Foo::f);
apply(&foo1, &foo2, &Foo::g);
}
- 成員函數(shù)指針的定義:void (Foo::*fun)()几莽,調(diào)用是傳遞的實(shí)參: &Foo::f;
- fun為類成員函數(shù)指針宅静,所以調(diào)用是要通過(guò)解引用的方式獲取成員函數(shù)*fun,即
(foo1->*fun)();