cpp c++11 可调用对象、包装器、绑定器 xingzhu 2025-01-20 2025-03-27
可调用对象
通俗来说,就是创建一个可以当函数作用一样调用的对象
包含一下几种:
是一个函数指针
是一个具有 operator()
成员函数的类对象(仿函数)
是一个可被转换为函数指针的类对象
是一个类成员函数指针或者类成员指针
函数指针 1 2 3 4 5 6 7 8 9 int print (int a, double b) { cout << a << " " << b << endl; return 0 ; } int (*func)(int , double ) = &print;(*func)(10 , 20 ); func (10 , 20 );
这里两种皆可
第一种很好理解,就是定义了一个指针指向函数地址,这里加不加 &
都一样,因为函数名就是地址,然后调用的时候进行解引用就拿到函数地址,就能进行调用了
第二种是 C++ 的简化,C++编译器允许直接使用函数指针进行调用,而不需要显式地解引用。编译器会自动理解 func(10, 20)
是通过函数指针 func
调用的,并将其翻译成等效的 (*func)(10, 20)
operator() 成员函数的类对象 (仿函数) 1 2 3 4 5 6 7 8 9 10 11 12 13 struct Test { void operator () (string msg) { cout << "msg: " << msg << endl; } }; int main () { Test t; t ("zhangsan" ); return 0 ; }
转换为函数指针的类对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using func_ptr = void (*)(int , string); struct Test { static void print (int a, string b) { cout << "name: " << b << ", age: " << a << endl; } void hello (int a, string b) { cout << a << b << endl; } operator func_ptr () { return print; } }; int main (void ) { Test t; t (19 , "zhangsan" ); return 0 ; }
语法是,operator 函数指针类型 () {}
调用是创建对象,传参数,然后就会自动转换为函数指针,执行 return
的函数地址
解释为啥 return hello
错误
如果写 return hello;
就是错误的,因为它是非静态成员函数,实例化对象后才有地址,且有个隐含的 this
指针,也就是和对象有关(我的瞎理解:对象都转换成函数指针去了,对象肯定就无了,那么就不能访问内部成员函数了)
两个指针类型不同,hello
函数是 void(Test::*)(int, string)
,而 func_ptr = void(*)(int, string)
,而静态成员函数 print = void(*)(int, string)
加个思考的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using func_ptr = void (*)(int , string); struct Test { static void print (int a, string b) { cout << "name: " << b << ", age: " << a << endl; } operator func_ptr () { return print; } void operator () (int x, string msg) { cout << "x: " << x << " " << "msg: " << msg << endl; } }; int main () { Test t; t (19 , "zhangsan" ); }
类成员函数指针或者类成员指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using func_ptr = void (*)(int , string); struct Test { void print (int a, string b) { cout << "name: " << b << ", age: " << a << endl; } int m_num; }; int main () { func_ptr = Test::print; using fptr = void (Test::*)(int , string); fptr = Test::print; int Test::*obj_ptr = &Test::m_num; Test t; (t.*fptr)(19 , "zhangsan" ); t.*obj_ptr = 1 ; cout << "number is: " << t.m_num << endl; }
包装器 std::function
是可调用对象的包装器
它是一个类模板,可以容纳除了类 (非静态)成员(函数)指针之外的所有可调用对象,对此绑定器可以解决
通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们
1 2 3 #include <functional> std::function<返回值类型(参数类型列表)> fun_name = 可调用对象;
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <functional> void print (string name, int age) { cout << "一般函数 " << name << " : " << age << endl; } using func_ptr = void (*)(string, int ); class Test { public : void operator () (string msg) { cout <<"函数重载 " << msg << endl; } operator func_ptr () { return world; } static void world (string name, int age) { cout << "函数指针 " << name << " : " << age << endl; } }; int main (void ) { function<void (string, int )> f1 = print; function<void (string, int )> f2 = Test::world; Test ta; function<void (string)> f3 = ta; Test tb; function<void (string, int )> f4 = tb; f1 ("zhangsan" , 3 ); f2 ("lisi" , 3 ); f3 ("wangwu" ); f4 ("sun" , 2 ); }
看了上述例子,感觉没啥用是吧,对此一举 实际上不是这么玩的,把它作为参数传参才是玩法,也就是回调函数 这里只是基础,需要先懂这,后面才好理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 void print (string name, int age) { cout << "一般函数 " << name << " : " << age << endl; } using func_ptr = void (*)(string, int ); class Test { public : void operator () (string msg) { cout <<"函数重载 " << msg << endl; } operator func_ptr () { return world; } static void world (string name, int age) { cout << "函数指针 " << name << " : " << age << endl; } }; class A { public : A (const function<void (string, int )>& f) : callback (f) {} void notify (string name, int age) { callback (name, age); } private : function<void (string, int )> callback; }; int main (void ) { function<void (string, int )> f1 = print; function<void (string, int )> f2 = Test::world; Test ta; function<void (string)> f3 = ta; Test tb; function<void (string, int )> f4 = tb; A aa (print) ; aa.notify ("zhangsan" , 3 ); A ab (Test::world) ; ab.notify ("lisi" , 3 ); A ac (tb) ; ac.notify ("sun" , 2 ); }
理解它
A 类通过初始化列表的方式初始化 callback
,也就成了 callback = print
,也就是上述第一个例子的 function<void(string, int)> f1 = print;
然后 A 类的对象调用 notify()
函数,也就是执行 callback ()
,这就是上述第一个案例的 f1("zhangsan", 3);
绑定器 std::bind
用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用 std::function
进行保存,并延迟调用到任何我们需要的时候
作用:
将可调用对象与其参数一起绑定成一个仿函数
将多元(参数个数为 n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数
占位符:placeholders::_1
,placeholders::_2
…..,这表示第一位参数列表的占位符,要被传入的参数覆盖
语法
1 2 3 4 5 auto f = std::bind (可调用对象地址, 绑定的参数/占位符);auto f = std::bind (类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
一般函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void output (int x, int y) { cout << x << " " << y << endl; } int main (void ) { bind (output, 1 , 2 )(); bind (output, placeholders::_1, 2 )(10 ); bind (output, 2 , placeholders::_1)(10 ); bind (output, 2 , placeholders::_2)(10 , 20 ); bind (output, placeholders::_1, placeholders::_2)(10 , 20 ); bind (output, placeholders::_2, placeholders::_1)(10 , 20 ); }
成员函数和变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Test {public : void output (int x, int y) { cout << "x: " << x << ", y: " << y << endl; } int m_number = 100 ; }; int main (void ) { Test t; function<void (int , int )> f1 = bind (&Test::output, &t, placeholders::_1, placeholders::_2); function<int &(void )> f2 = bind (&Test::m_number, &t); f1 (520 , 1314 ); f2 () = 2333 ; cout << "t.m_number: " << t.m_number << endl; }
注意上述的成员变量那里的 f2
与 auto
的 f2
不等价,auto
自动类型推导为仿函数,另一个是包装器类型,因为 bind
返回值是一个仿函数,上面的成员函数等等同理
说明:本文是在 https://subingwen.cn/ 学习过程中的总结,这个 up 主 B 站讲得很好 !!
xingzhu
keep trying!keep doing!believe in yourself!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 星竹 !