博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++Primer : 第十四章 :函数调用运算符
阅读量:2433 次
发布时间:2019-05-10

本文共 6984 字,大约阅读时间需要 23 分钟。

函数调用运算符

函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。
如果类定义了调用运算符,则该类的对象称为函数对象。
含有状态的函数对象类
函数对象除了operator()之外也可以包含其他成员。函数对象类通常含有一些数据成员,这些成员被用于定制调用运算符中的操作。
定义一个打印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; //用于将不同输出隔开的字符};

练习14.33:一个重载的函数调用运算符应该接受几个运算对象?

0个或者多个

练习14.34:定义一个函数对象类,令其执行if-then-else的操作:该类的调用运算符接受三个形参,它首先检查第一个形参,如果成功返回第二个形参的值,如果不成功返回第三个形参的值。

class myclass {
public: myclass(){
} myclass(int i1, int i2, int i3): i1(i1), i2(i2), i3(i3){
} int operator()(int i1, int i2, int i3) {
return i1 ? i2 : i3; }private: int i1, i2, i3;};

练习14.35:编写一个类似于PrintString的类,令其从istream中读取一行输入,然后返回一个表示我们所读内容的string。如果读取失败,返回空string。

class ReadString {
public: ReadString(istream &is = cin): is(is){
} string operator()() {
string line; if (!getline(is, line)) {
line == " "; } return line; }private: istream& is;};

练习14.36:使用前一个练习定义的类读取标准输入,将每一行保存为vector的一个元素。

void TestReadString() {
ReadString rs; vector
vec; while (true) {
string line = rs(); if (!line.empty()) vec.push_back(line); else break; }}

练习14.37:编写一个类令其检查两个值是否相等。使用该对象及标准库算法编写程序,令其替换某个序列中具有给定值的所有实例。

class IntCompare {
public: IntCompare(int v):val(v){
} bool operator()(int v) {
return val == v; }private: int val;};int main() {
vector
vec = {
1,2,3,2,1 }; const int oldvalue = 2; const int newvalue = 200; IntCompare icmp(oldvalue); std::replace_if(vec.begin(), vec.end(), icmp, newvalue); return 0;}

lambda是函数对象

当我们编写一个lambda后,编译器将该表达式翻译成一个未命名对象。在lambda表达式产生的类中含有一个重载的函数调用运算符,例如:对于我们传递给stable_sort作为其最后一个实参的lambda表达式来说:

//根据单词的长度对其进行排序,对于长度相同的单词按字母表顺序排序stable_sort(words.begin(), words.end(), [](const string& a, const stirng& b) {
return a.size() < b.size(); });//行为类似于下面这个类的一个未命名对象class ShorterString {
public: bool operator()(const string& s1, const string& s2)const {
return s1.size() < s2.size(); }};

默认情况下lambda不能改变它捕获的变量。因此在默认情况下,由lambda产生的类当中的函数调用运算符是一个const成员函数。如果lambda被声明为可变的,则调用运算符就不是const的了。

用这个类代替lambda表达式后:

stable_sort(words.begin(), words.end(), ShorterString());

表示lambda及相应捕获行为的类

当一个lambda表达式通过引用捕获变量时,将由程序确保lambda执行时引用所引的对象确实存在。因此,编译器可以直接使用该引用而无需在lambda产生的类中将其存储为数据成员。
相反,通过值捕获的变量被拷贝到lambda中。因此,这种lambda产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数,令其使用捕获的变量的值来初始化数据成员。

//获得第一个指向满足条件元素的迭代器,该元素满足size() is >= szauto wc = find_if(words.begin(), words.end(), [sz](const string& a) {
return a.size() >= sz; });//该lambda表达式产生的类将形如:class SizeComp {
SizeComp(size_t n):sz(n){
} //该形参对应捕获的变量 //该调用运算符的返回类型、形参和函数体都与lambda一致 bool operator()(const string& s) const {
return s.size() >= sz; }private: size_t sz;};

和ShorterString类不同,上面这个类含有一个数据成员以及一个用于初始化该成员的构造函数。这个合成的类不含有默认构造函数,因此要想使用这个类必须提供一个实参.

auto wc = find_if(words.begin(), words.end(), SizeComp(sz));

练习14.38:编写一个类令其检查某个给定的string对象的长度是否与一个阈值相等。使用该对象编写程序,统计并报告在输入的文件中长度为1的单词有多少个、长度为2的单词有多少个、……、长度为10的单词有多少个。

#include 
#include
#include
#include
using namespace std;class StrLenIs {
public: StrLenIs(int len): len(len){
} bool operator()(const string& str) {
return str.length() == len; }private: int len;};void ReadStr(istream& is, vector
& vec) {
string word; while (is >> word) {
vec.push_back(word); }}int main() {
vector
vec; ReadStr(cin, vec); const int MinLen = 1; const int MaxLen = 10; for (int i = MinLen; i <= MaxLen; i++) { StrLenIs Is(i); cout << "len: " << i << ",cnt: " << count_if(vec.begin(), vec.end(), Is) << endl; } return 0;}

练习14.39:修改上一题的程序令其报告长度在1至9之间的单词有多少个、长度在10以上的单词又有多少个。

class StrLenBetween {
public: StrLenBetween(int MinLen, int MaxLen) : MinLen(MinLen), MaxLen(MaxLen) {
} bool operator()(const string& str) {
return str.length() >= MinLen && str.length() <= MaxLen; }private: int MinLen; int MaxLen;};class StrNotShorterThan {
public: StrNotShorterThan(int MinLen) : MinLen(MinLen) {
} bool operator()(const string& str) {
return str.length() >= MinLen; }private: int MinLen;};//extern ReadStr(istream& is, vector
& vec);int main() {
vector
vec; ReadStr(cin, vec); StrLenBetween slb(1, 9); StrNotShorterThan snst(10); cout << "len 1~9: " << count_if(vec.begin(), vec.end(), slb) << endl; cout << "len >= 10: " << count_if(vec.begin(), vec.end(), snst) << endl; return 0;}

练习14.40:重新编写10.3.2节的biggies函数,使用函数对象类替换其中的lambda表达式。

class IsShorter {
public: bool operator()(const string& s1, const string& s2) {
return s1.size() < s2.size(); }};class StrNotShorterThan {
public: StrNotShorterThan(int MinLen) : MinLen(MinLen) {
} bool operator()(const string& str) {
return str.length() >= MinLen; }private: int MinLen;};class PrintString {
public: void operator()(const string& str) {
cout << str << " "; }};void biggies(vector
& words, vector
::size_type sz) {
elimDups(words); IsShorter is; stable_sort(words.begin(), words.end(), is); StrNotShorterThan snst(sz); auto wc = find_if(words.begin(), words.end(), snst); auto count = words.size() - wc; cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer " << endl; PrintString ps; for_each(wc, words.end(), ps); cout << endl;}

练习14.41:你认为C++11新标准为什么要增加lambda

在C++11中,lambda是通过匿名的函数对象来实现的,因此我们可以把lambda看作是对函数对象在使用方式上的简化。

标准库定义的函数对象

标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。这些类都被定义成模板的形式,我们可以为其指定具体的应用类型,这里的类型即调用运算符的形参类型。例如,plus<string>令string加法运算符作用于string对象,plus<Sales_data>对Sales_data对象执行加法运算。此类函数对象类型定义在functional头文件中。

在算法中使用标准库函数对象

//传入一个临时的函数对象用于执行两个string对象的>比较运算sort(svec.begin(), svec.end(), greater
());

需要特别注意的是,标准库规定其函数对象对于指针同样适用。比较两个无关指针将产生未定义的行为,然而可以通过比较指针的内存地址来sort指针的vector。直接这样做会产生未定义行为,因此我们可以使用一个标准函数对象来实现该目的:

vector
nameTable; //指针的vector//错误:nameTable中的指针彼此之间没有关系,所以
<将产生未定义的行为sort(nametable.begin(), nametable.end(), [](string* a, string* b) { return a < b; }); 正确:标准库规定指针的less是定义良好的sort(nametable.begin(), less
());

练习14.42:使用标准库函数对象及适配器定义一条表达式,令其

(a)统计大于1024的值有多少个
(b)找到第一个不等于pooh的字符串
(c)将所有值乘以2

count_if(vec.begin(), vec.end(), bind(greater
(), 1024));find_if(vec.begin(), vec.end(), bind(not_equal_to
(), "pooh"));transform(vec.begin(), vec.end(), vec.begin(), bind(multiplies
(), 2));

练习14.43:使用标准库函数对象判断一个给定的int值是否能被int容器中的所有元素整除

bool divide(vector
&ivec, int dividend){
return count_if(ivec.begin(), ivec.end(), bindlst(modulus
, dividend)) == 0;}

练习14.44:编写一个简单的桌面计算器使其能处理二元运算

#include 
#include
#include
#include
using namespace std;map
> binops = {
{
"+", plus
()}, { "-", minus
()}, { "/", divides
()}, { "%", modulus
()}};int main() { int a, b; string op; cin >> a >> op >> b; cout << binops[op](a, b) << endl; return 0;}

转载地址:http://ltxmb.baihongyu.com/

你可能感兴趣的文章
【Java】【算法】——算法篇
查看>>
【Java】【数据库】知识重点——数据库篇
查看>>
【Java】知识重点——消息队列篇
查看>>
【Java】学习总结 —— HashMap之put()方法实现原理
查看>>
【计算机网络】【TCP】如何讲清楚Tcp的三次握手和四次挥手?
查看>>
【Java】-- Java核心知识点总结
查看>>
【数据库】SQL之重点知识点总结
查看>>
【计算机网络】计算机网络知识总结
查看>>
【Java】【Web】JavaWeb相关知识总结 2018-9-17
查看>>
【数据库】突破单一数据库的性能限制——数据库-分库分表总结 2018-9-20
查看>>
Slurm——作业调度处理
查看>>
Lustre 维护
查看>>
Lustre—磁盘配额测试
查看>>
SSH加密密码中的非对称式密码学
查看>>
Mac Redis安装入门教程
查看>>
python3安装教程配置配置阿里云
查看>>
Mac快捷键和实用技巧
查看>>
Git的多人协作和分支处理测试
查看>>
mysql索引回表
查看>>
iterm2 保存阿里云登陆并防止断开连接
查看>>