C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象
重载操作符与转换
--调用操作符和函数对象
引言:
可以为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符!
struct absInt { int operator() (int val) { return val > 0 ? val : -val; } };
通过为类类型的对象提供一个实参表而使用调用操作符,所用的方式看起来系那个一个函数调用:
absInt absObj; int i = -1; cout << absObj(i) << endl;
尽管absObj是一个对象而不是函数,我们仍然可以“调用”该对象,效果是运行有absObj对象定义的重载调用操作符,该操作接受一个int值并返回它的绝对值。
函数调用操作符必须声明为成员函数,一个类可以定义函数调用操作符的多个版本,由形参的数目或类型加以区别。
定义了调用操作符的类,其对象常称为函数对象,即它们是行为类似函数的对象!
//P450 习题14.31 struct ifElse { int operator() (int x,int y,int z) { return x ? y : z; } }; int main() { ifElse ifObj; cout << ifObj(0,1,2) << endl; cout << ifObj(1,1,2) << endl; }
一、将函数对象用于标准库算法
函数对象经常用作通用算法的实参。回忆曾经的一段程序:
bool GT6(const string &str) { return str.size() > 6; }
使用GT6作为传给count_if算法的实参,以计算使GT6返回true的单词数目:
vector<string>::size_type wc = count_if(words.begin(),words.end(),GT6);
1、函数对象可以比函数更灵活
通过将GT6定义为带函数调用成员的类,可以使得传递的string与我们想要的长度进行测试,这样,可以使用同一段代码对不同长度的字符串进行计数:
class GT_cls { public: GT_cls(size_t val = 0):bound(val){} bool operator() (const string &str) { return str.size() >= bound; } private: std::string::size_type bound; };
2、使用GT_cls函数对象
cout << count_if(words.begin(),words.end(),GT_cls(6)) << " words 6 characters or longer" << endl;
这个count_if调用传递一个GT_cls类型的临时对象而不再是名为GT6的函数。用整型值6来初始化那个临时对象,构造函数将这个值存储在bound成员中。现在,count_if每次调用它的函数形参时,它都使用GT_cls的调用操作符,该调用操作符根据bound的值测试其string实参的长度。
使用函数对象,容易修改程序以根据其他值进行测试,只需为传给count_if的对象改变构造函数实参即可。
//计算长度在5个字符以上的单词数 cout << count_if(words.begin(),words.end(),GT_cls(5)) << " words 6 characters or longer" << endl;
还可以用来计算长度在1到10个字符的单词数:
for (size_t i = 1; i != 11; ++i) { cout << count_if(words.begin(),words.end(),GT_cls(i)) << " words " << i << " characters or longer" << endl; }
如果使用函数替代函数对象来编写这个程序,可能需要编写10个(⊙﹏⊙b汗)不同的函数,每个函数测试一个不同的值!
//P451 习题14.33 class GT_cls { public: GT_cls(size_t val = 0):bound(val) {} bool operator() (const string &str) { return str.size() > bound; //将>=改为> } private: std::string::size_type bound; }; int main() { vector<string> words; ifstream inFile("input"); string word; while (inFile >> word) { words.push_back(word); } vector<string>::iterator findIter = find_if(words.begin(),words.end(),GT_cls(5)); if(findIter != words.end()) { cout << *findIter << endl; } else { cout << "Have No Found!" << endl; } }
//习题14.34 struct IsSame { bool operator() (const string &s1,const string &s2) { return s1 == s2; } }; int main() { IsSame isSame; cout << isSame("xiao","fang") << endl; cout << isSame("Ha","Ha") << endl; }
//习题14.35/36 class BET_cls { public: BET_cls(size_t Max = 0,size_t Min = 0):max(Max),min(Min) {} bool operator() (const string &str) { return str.size() <= max && str.size() >= min; } private: std::string::size_type max; std::string::size_type min; }; class GT_cls { public: GT_cls(size_t size = 0):bound(size) {} bool operator() (const string &str) { return str.size() >= bound; } private: std::string::size_type bound; }; int main() { ifstream inFile("input"); vector<string> words; string word; while (inFile >> word) { words.push_back(word); } cout << "The is " << count_if(words.begin(),words.end(),BET_cls(9,1)) << " words`s size is between 1 and 9" << endl; cout << "And " << count_if(words.begin(),words.end(),GT_cls(10)) << " words`s size is equal to 10 or longer" << endl; }
二、标准库定义的函数对象
标准库定义了一组算术、关系与逻辑函数对象类。标准库还定义了一组函数适配器,使我们能够特化或者扩展标准库所定义的以及自定义的函数对象类。这些标准库函数对象类型是在functional头文件中定义的。
标准库函数对象 | ||
---|---|---|
类型 | 函数对象 | 所应用的操作符 |
算术函数对象类型 | plus<Type> | + |
minus<Type> | - | |
multiplies<Type> | * | |
divides<Type> | / | |
modulus<Type> | % | |
关系函数对象类型 | negate<Type> | - |
equal_to<Type> | == | |
not_equal_to<Type> | != | |
greater<Type> | > | |
greater_equal<Type> | >= | |
less<Type> | < | |
less_equal<Type> | <= | |
逻辑函数对象类型 | logical_and<Type> | && |
logical_or<Type> | || | |
logical_not<Type> | ! |
1、每个类表示一个给定操作符
每个标准库函数对象类表示一个操作符,即每个类都定义了应用命名操作的调用操作符。如plus是表示加法操作符的模板类型,plus模板中的调用操作符对一对操作数使用+运算!
//假设Type类型为int,简单实现一下plus class Plus { public: int operator() (int a,int b) { return a + b; } //... };
有两个一元函数对象类:一元减(negate<Type>))和逻辑非(logical_not<Type>))。其余的标准库函数对象都是表示二元操作符的二元函数对象类。为二元操作符定义的调用操作符需要两个给定类型的形参,而一元函数对象类型定义了接受一个实参的调用操作符。
2、表示操作数类型的模板类型
每个函数对象类都是一个类模板,我们需要为该模板提供一个类型:
plus<string> strAdd; plus<int> intAdd;
应用:
plus<string> strAdd; cout << strAdd("Hello ","World\n"); plus<int> intAdd; negate<int> intNegate; cout << intAdd(10,20) << endl; int sum = intAdd(10,intNegate(20)); cout << sum << endl;
3、在算法中使用标准库函数对象
函数对象常用于覆盖算法使用的默认操作符。例如,sort默认使用operator<按升序对容器进行排序。为了按降序对容器进行排序,可以传递函数对象greater。该类将产生一个调用操作符,调用基础对象的大于操作符。
sort(strVec.begin(),strVec.end(),greater<string>()); //降序排序
//附:完整程序测试 void printVec(const vector<string> &vec) { for (vector<string>::const_iterator iter = vec.begin(); iter != vec.end(); ++iter) { cout << *iter << ‘\t‘; } cout << endl; } int main() { ifstream inFile("input"); vector<string> strVec; string word; while (inFile >> word) { strVec.push_back(word); } sort(strVec.begin(),strVec.end()); //升序排序 printVec(strVec); sort(strVec.begin(),strVec.end(),greater<string>()); //降序排序 printVec(strVec); }
第二个sort函数的第三个实参greater<string>是一个临时对象,是一个将>操作符应用于两个string操作数的函数对象。
三、函数对象的函数适配器
标准库提供了一组函数适配器,用于特化和扩展一元和二元函数对象。函数适配器分为:
1、绑定器:是一种函数适配器,它通过将一个操作数绑定到给定值,而将二元函数对象转换为一元函数对象。有:bind1str,bind2nd.
每个绑定器接受一个函数对象和一个值,bind1st将给定值绑定到二元函数对象的第一个实参,bind2nd将给定值绑定到二元函数对象的第二个实参。
//计算容器中所有小于或等于 10 的元素的个数 count_if(vec.begin(),vec.end(),bind2nd(less_equal<int>(),10));
该适配器返回一个函数对象,该对象用10作右操作数应用<=操作符,调用计算输入范围中小于或等于10的元素的个数。
2、求反器:将谓词函数的真值求反。有:not1和not2。
not1将一元函数对象的真值求反,not2将二元函数对象的真值求反。
//是对不 <= 10[即:> 10] 的那些元素进行计数 count_if(vec.begin(),vec.end(), not1(bind2nd(less_equal<int>(),10)));
//P453 习题14.37 //(a) int main() { vector<int> ivec; ifstream inFile("input"); int val; while (inFile >> val) { ivec.push_back(val); } typedef vector<int>::iterator iterType; iterType iter = ivec.begin(); while ((iter = find_if(iter,ivec.end(),bind2nd(greater<int>(),10))) != ivec.end()) { cout << *iter << endl; ++ iter; } }
//(b) int main() { vector<string> strVec; ifstream inFile("input"); string val; while (inFile >> val) { strVec.push_back(val); } typedef vector<string>::iterator iterType; iterType iter = strVec.begin(); //主要的改动在于是换成了not_equal_to while ((iter = find_if(iter,strVec.end(), bind2nd(not_equal_to<string>(),"pooh"))) != strVec.end()) { cout << *iter << endl; ++ iter; } }
//(3) int main() { vector<int> ivec; ifstream inFile("input"); int val; while (inFile >> val) { ivec.push_back(val); } typedef vector<int>::iterator iterType; multiplies<int> intMulti; for (iterType iter = ivec.begin(); iter != ivec.end(); ++iter) { *iter = intMulti(*iter,2); } /**或者使用标准库算法:transform *transform(ivec.begin(),ivec.end(),ivec.begin(),bind2nd(multiplies<int>(),2)); */ for (iterType iter = ivec.begin(); iter != ivec.end(); ++iter) { cout << *iter << endl; } }
//习题14.39 int main() { ifstream inFile("input"); vector<string> words; string word; while (inFile >> word) { words.push_back(word); } cout << "And " << count_if(words.begin(),words.end(),GT_cls(3)) << " words`s size is equal to 3 or longer" << endl; greater_equal<string::size_type> sizeGreEQ; string::size_type wc = 0; for (vector<string>::iterator iter = words.begin(); iter != words.end(); ++iter) { if (sizeGreEQ(iter -> size(),3)) ++ wc; } cout << "And " << wc << " words`s size is equal to 3 or longer" << endl; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。