《C++ Primer》读书笔记第十四章-2-函数调用运算符
来源:互联网 发布:淘宝vip专享 编辑:程序博客网 时间:2024/06/10 13:58
笔记会持续更新,有错误的地方欢迎指正,谢谢!
类包含状态所以比普通函数更灵活,如果类定义了调用运算符(),那么该类的对象叫作函数对象,因为可以调用这种对象,所以,我们说这些对象的行为像函数一样。
带状态的函数对象类
我们要写个打印string的类,默认情况下,我们的类会把内容写入cout中,每个string用空格隔开,也允许用户提供其他可写入的流及分隔符:
class PrintString{public: PrintString(ostream &o = cout, char c = ' ') : os(o), sep(c) {} void operator() (const string &s) const {os << s << sep;}private: ostream &os; //用于写入的目的流 char sep; //分隔字符};
上面这个类不难看懂吧,虽然涉及蛮多知识的,但是我们都介绍过了,下面我们来调用试试:
当定义PrintString对象时,对于分隔符及输出流,既可使用默认值也可提供自己的值。
string s = "hehe";PrintString printer;//使用默认值,打印到coutprinter(s); //在cout中打印s,后面跟一空格PrintString errors(cerr, '\n');//使用自己提供的值,打印到cerrerrors(s); //在cerr中打印s,并且后面默认跟的空格,变成了换行
函数对象PrintString(cerr, '\n')
还可以作为泛型算法的实参:
for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));
第三个参数是PrintString的一个临时对象(看着是不是很像lambda,不过它没有捕获什么)
Lambda是函数对象
我们来回忆下之前的lambda表达式,根据单词的长度对其进行排序,对于长度相同的单词按照字典序(字母表顺序)排序:
stable_sort(words.begin(), words.end(), [](const string &a, const string &b) {return a.size() < b.size();});
那么归根结底,编译器是怎么处理神奇的lambda的呢?其实直接把lambda当成一个未命名类的未命名对象(这个类的对象还是个函数对象)
所以,我们可以用一个函数对象去替代上面的lambda:
class ShorterString//函数对象{public: bool operator()(const string &s1, const string &s2) const { return s1.size()<s2.size(); }};
有了这个类后,我们就可以这样写:
stable_sort(words.begin(), words.end(), ShorterString())
第三个参数是新构建的ShorterString临时对象。
刚刚我们是用函数对象去替代了一个没有捕获的lambda,接下来我们要来个有捕获列表的lambda:
///获得第一个size大于给定sz的元素的迭代器auto wc = find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz;});
那我们相应的类是这样:
class SizeComp{public: SizeComp(size_t n) : sz(n){} //要写个构造函数,用来“捕获变量” bool operator()(const string &s) const {return s.size()>=sz;}private: size_t sz;};
替换后:
auto wc = find_if(words.begin(), words.end(),SizeComp(sz));
标准库定义的函数对象
标准库函数对象:一组表示算术运算符、关系运算符、逻辑运算符的类。每个类分别定义了执行命名操作的调用运算符;都是模版类。要用的时候再查好了,基本都定义在头文件functional中。
举个例子:
vector<string> svec = {"f", "z"};sort(svec.begin(), svec.end(), greater<string>()); //降序排列第三个参数是未命名的函数对象
可调用对象与function
我们来搞点事情:
//四则运算int add(int i, int j){return i + j;} //普通函数auto mod = [](int i, int j){return i % j;} //lambdastruct divide //函数对象类{ int operator()(int de, int di){return de / di;}};
有没有发现,调用以上这些东西,都是同一种形式:int(int, int)
所以,我们想定义一个函数表(也就是使用可调用的对象构建一个简单的桌面计算器),从表中查找要调用的函数,在C++里面,我们可以用map,但是,又不行,为啥呢:
map<string, int(*)(int, int)> binops; //第一个参数是string,第二个参数是函数指针,而不是(int, int)binops.insert({"+", add}); //然而其他的不是函数类型,放不进map里
于是我们要有新东西来解决这个问题了
标准库function类型
直接抛代码,简单来说就是把function作为一个模板,这定义在functional头文件中:
unction<int(int, int)>*///我们声明了一个function类型,//表示接受两个int、返回一个int的可调用对象
我们可以把刚刚那些函数全跟它匹配:
function<int(int, int)> f1 = add; //函数指针function<int(int, int)> f2 = divide(); //函数对象类的对象function<int(int, int)> f3 = [](int i, int j){return i*j;}; // lambdacout << f1(4, 2); //打印6cout << f2(4, 2); //2cout << f3(4, 2); //8
有了这个神器,我们就可以建立函数表了:
map<string, function<int(int, int)>> binops;//可以理解为后一个参数指定了函数类型(函数名正好不在函数类型里面)
我们来重新初始化一下,给它点内容:
map<string, function<int(int, int)>> binops = { //四则运算 {“+”, add}, {"-", atd::minus<int>()}, //标准库函数对象 {"*", [](int i, int j){return i*j;}}, //未命名的lambda {"/", divide()}, {"%", mod}};
有了这个函数表,我们就可以很方便很直观地调用了:
binops["+"](10, 5); //15binops["-"](10, 5); //5binops["*"](10, 5); //50binops["/"](10, 5); //2binops["%"](10, 5); //0
- 《C++ Primer》读书笔记第十四章-2-函数调用运算符
- 《C++ Primer》读书笔记第十四章-1-重载IO、算术、关系等运算符
- 《C++ Primer》读书笔记第十四章-3-类型转换运算符、二义性
- c++ primer读书笔记-第十四章 重载操作符与转换
- C++Primer习题第十四章
- 读书笔记《C++ Primer》第五版——第十四章 重载运算与类型转换
- 《C++primer(第五版)》学习之路-第十四章:重载运算与类型转换
- C++primer第五版笔记-第十四章重载运算与类型转换
- c++primer(第五版) 第十四章 重载运算与类型转换习题答案
- c++primer第十四章重载运算与类型转换小结-14
- C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符
- c++primer第五版第十四章练习
- C++primer 第十四章笔记 初稿
- 《C++Primer》读书笔记--函数模板
- C和指针读书笔记(第十四章)
- C++ Pirmer : 第十四章 : 重载运算符与类型转换之函数调用运算符与标准库的定义的函数对象
- C++Primer 读书笔记 第2章 浏览
- C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配
- OProfile Tutorial
- exploiting-leaked-thread-handle
- Lay ui 的时间插件 总是报错 Uncaught TypeError: laydate.render is not a function
- kotlin反射中的类引用
- ++i和i++的区别(C++)
- 《C++ Primer》读书笔记第十四章-2-函数调用运算符
- 手机设备的唯一标识符
- Kotlin学习笔记2-1 基础-基本类型
- python数值类型及输入输出
- 用C语言开发一个BT下载软件 (二) ------ 算法和策略
- 用C语言开发一个BT下载软件 (三) ------ 系统结构设计
- 《C++ Primer》读书笔记第十四章-3-类型转换运算符、二义性
- 网易云课堂推了个“行家计划”,要2年培育100位收入超500万的知识行家
- 亚马逊收购无线摄像头初创公司Blink