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实參的长度。
使用函数对象,easy修改程序以依据其它值进行測试,仅仅需为传给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; }