C++11新增了很多东西,只有三个可以称为神器:
1 智能指针
2 移动语义和
3 可调用对象的包装器和绑定器
下面详解可调用对象、包装器std::function、绑定器std::bind
以及它们最有价值的应用场景:
可变函数和参数、回调函数、取代虚函数
可调用对象:
在C++中,可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象。
可调用对象有类型,可以用指针存储它们的地址,可以被引用(类的成员函数除外)
一、普通函数
普通函数类型可以声明函数、定义函数指针和函数引用,但是不能定义函数的实体。
#include <iostream> using namespace std; using Fun = void(int, const string &); //普通函数类型的别名 Fun show; //声明普通函数,这一行效果等同于下面这一行 // void show(int, const string &);//声明普通函数 void test() { show(1, "a1"); //直接调用普通函数 // 1 C风格的 void (*pf)(int, const string &) = show; //声明函数指针,指向普通函数 pf(2, "a2"); void (&rf)(int, const string &) = show; //声明函数引用,引用普通函数 rf(3, "a3"); // 2 C++风格的 //声明函数指针,指向普通函数 Fun *pf2 = show; pf2(4, "a4"); //声明函数引用,引用普通函数 Fun &rf2 = show; rf2(5, "a5"); } int main() { test(); return 0; } //定义普通函数 void show(int a, const string &b) { cout << a << " " << b << endl; } //以下代码是错误的,不能用函数类型定义函数的实体. // Fun show(int a, const string &b) // { // cout << a << " " << b << endl; // } /*1 a1 2 a2 3 a3 4 a4 5 a5 */
讯享网
函数指针我们经常使用,很少使用函数引用,引用的本质是指针,所以,函数指针和函数引用是同一个东西
上面的demo重点表达函数类型和函数对象这两个概念。
在C++中,函数是一种数据类型,函数的实体被看成对象,而不只是一段代码。
函数是对象,可以调用的对象,或者叫函数对象,这种思路和传统的不一样
二、类的静态成员函数
类的静态成员函数和普通函数本质上是一样的,把普通函数放在类中而已
讯享网#include <iostream> using namespace std; using Fun = void(int, const string &); //别名 struct AA { static void show(int a, const string &b) { cout << a << " " << b << endl; } }; void test() { AA::show(1, "a1"); //直接调用静态成员函数 // 1 C风格的 void (*pf)(int, const string &) = AA::show; //声明函数指针 pf(2, "a2"); void (&rf)(int, const string &) = AA::show; //声明函数引用 rf(3, "a3"); // 2 C++风格的 //声明函数指针 Fun *pf2 = AA::show; pf2(4, "a4"); //声明函数引用 Fun &rf2 = AA::show; rf2(5, "a5"); } int main() { test(); return 0; }
三、仿函数
仿函数的本质是类,调用的代码像函数。
仿函数的类型就是类的类型。
普通类创建的对象为什么不是可调用的对象呢?
因为它不能像函数那样直接调用,就扣字面意思,调用它的成员函数是另外一回事。
#include <iostream> using namespace std; struct AA { void operator()(int a, const string &b) { cout << a << " " << b << endl; } }; void test() { AA aa; aa(1, "1"); //用对象调用仿函数 AA() (2, "2"); //用匿名对象调用仿函数,这里换行是VSCODE格式化就这样,不报错 AA &raa = aa; //引用函数 raa(3, "3"); //用对象的引用调用仿函数 } int main() { test(); return 0; }
仿函数是类,当然可以创建引用
四、lambda函数
lambda函数的本质是仿函数,仿函数的本质是类
讯享网#include <iostream> using namespace std; void test() { auto func = [](int a, const string &b) { cout << a << " " << b << endl; }; func(1, "a"); //用lambda对象调用仿函数 //引用lambda auto &func2 = func; func2(2, "b"); //引用lambda对象的引用调用仿函数 } int main() { test(); return 0; }
五、类的非静态成员函数 (这个与众不同,所以需要包装器和绑定器)
类的非静态成员函数有地址,但是,只能通过类的对象 才能调用它,所以C++对它做了特别处理。
类的非静态成员函数只有指针类型,没有引用类型,不能引用。
C++有6种可调用对象,只有类的非静态成员函数与众不同,正因为它的不同,所以才需要可调用对象的包装器和绑定器。但是它确实最重要的可调用对象,不能没有它。
#include <iostream> using namespace std; //普通函数 void show(int a, const string &b) { cout << a << " " << b << endl; } struct AA { void show(int a, const string &b) { cout << a << " " << b << endl; } }; void test() { AA aa; aa.show(1, "a1"); // 1 传统C风格 void (*pft1)(int, const string &) = show; //普通函数指针,普通函数可以省略&,也可以不省略 void (*pft2)(int, const string &) = &show; //普通函数指针,普通函数可以省略&,也可以不省略 void (AA::*pf)(int, const string &) = &AA::show; //定义类的成员函数的指针,&AA::show,取地址的符号&不能省略 (aa.*pf)(2, "a2"); //用类的成员函数的指针调用成员函数 // 2 C++11风格 //类成员函数的指针类型 起别名 using pFun = void (AA::*)(int, const string &); pFun pf1 = &AA::show; //让类成员函数的指针指向类的成员函数的地址 (aa.*pf1)(11, "aa1"); //用类成员函数的指针调用类的成员函数 } int main() { test(); return 0; }
这种调用的方式与其它可调用的对象不一样,其它可调用对象用变量名(对象名)就可以直接调用,而类成员函数这种可调用对象,需要对象名和函数指针变量名组合在一起才能调用。
这种区别给模板带来了麻烦,代码不能统一,模板没法写。
怎么解决这个问题,后面包装器绑定器再说。
六、可被转换为函数指针的类对象 (意义不大)
类可以重载类型转换运算符
语法:
operator 数据类型()
如果数据类型时函数指针或者函数引用类型,那么该类实例也将成为可调用对象。
它的本质是类,调用的代码像函数。
为什么说这种方式没什么意义呢?
因为这个转换函数只能返回普通全局函数和类的静态成员函数,不能返回类的非静态成员函数,普通全局函数和类的静态成员函数本身就是可调用对象,直接调用就行了,用不着用这个,没任何好处。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/117821.html