重载操作符与转换

--调用操作符和函数对象

引言:

能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符!

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;
}

C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象的更多相关文章

  1. C++ Primer 学习笔记_63_重载运算符和转换 --转换和类类型【上】

    重载运算符和转换 --转换与类类型[上] 引言: 在前面我们提到过:能够用一个实參调用的位 unsignedchar 相同范围的值,即:0到255. 这个类能够捕获下溢和上溢错误,因此使用起来比内置u ...

  2. C++ Primer 学习笔记_60_重载操作符与转换 --赋值、下标、成员訪问操作符

    重载操作符与转换 --赋值.下标.成员訪问操作符 一.赋值操作符 类赋值操作符接受类类型形參,通常该形參是对类类型的const引用,但也能够是类类型或对类类型的非const引用.假设未定义这个操作符, ...

  3. C++ Primer 学习笔记_61_重载操作符与转换 --自增/自减操作符

    重载操作符与转换 --自增/自减操作符 引言: 自增,自减操作符常常由诸如迭代器这种类实现,这种类提供相似于指针的行为来訪问序列中的元素.比如,能够定义一个类,该类指向一个数组并为该数组中的元素提供訪 ...

  4. C++ Primer学习笔记(三) C++中函数是一种类型!!!

    C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...

  5. C++ Primer学习笔记(二)

    题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接  C++ Primer学习笔记(一)   27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...

  6. C++primer学习笔记(二)——Chapter 4

    4.1  Fundamentals 1.Basic Concepts (1)操作符分为一元,二元或者三元操作符: (2)复杂的表达式中含有很多操作符时: 规则一:分为不同的级别,级别高的先运行: 规则 ...

  7. C++ Primer学习笔记2--c++标准库中的 vector、string 和 bitset 类型

    一.string    #include <string>  using std::string    初始化函数:    string s1;        默认构造函数 s1 为空串 ...

  8. [C/C++] C++ Primer学习笔记

    记录下自己掌握不清楚的概念和用法... Day 1 endl:具有输出换行的效果,并刷新与设备相关联的缓冲区. 注:在调试程序过程中插入的输出语句都应刷新输出流,否则可能会造成程序崩溃,将会导致程序出 ...

  9. C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]

    面向对象编程 --句柄类与继承[续] 三.句柄的使用 使用Sales_item对象能够更easy地编写书店应用程序.代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对 ...

随机推荐

  1. HDU 4293 Groups

    模型挺好的dp题,其实这道题就是建一个模型然后就很容易想到递推过程了,我们可以把每个人的描述,存到数组a中,a[l][r]表示左边有l个,到第r个这个人所在一层停止...然后就可以写出转移状态方程了. ...

  2. boost::asio网络传输错误码的一些实验结果(recv error_code)

    错误码很重要,可以由此判断网络连接到底发生了神马事情,从而驱动高层逻辑的行为.只有笼统的错误码判断的网络层是不够规范的,鄙人觉得有些错误码还是需要在网络层就区分开的,特此记录一些当前实验的错误码以及发 ...

  3. vc 在edit控件中动态插入数据滚动显示

    内存从网上论坛摘抄整理 思路:给控件设置多行属性,设置垂直滚动条,Auto Vscroll设置为true,放入文本后把插入点设置到末尾 pEdit->LineScroll(pEdit->G ...

  4. 在cocos2d-x jsb/html5中设置触摸代理的方法

    和官方的说明不同,js binding的很多api和ch5版是不一样的.遇到不一样的就需要我们努力去看源码寻找了. 主要是以下几个文件 cocos2d_specifics.cpp cocos2d_sp ...

  5. How to Design Programs, Second Edition

    How to Design Programs, Second Edition How to Design Programs, Second Edition

  6. Java程序员须知的七个日志管理工具(转)

    Splunk vs. Sumo Logic vs. LogStash vs. GrayLog vs. Loggly vs. PaperTrails vs. Splunk>Storm 英文原文:T ...

  7. SilkTest高级进阶系列6-motif tag

    看SilkTest代码的时候不小心看到winclass.inc里面的一些类申明使用的是motif tag,例如: [-] winclass MessageBoxClass : DialogBox [ ...

  8. ios应用接入微信开放平台

    前几天试了一下服务端接入微信公众平台,昨天又看了一下APP接入开放平台 开放平台和公众平台的差别 公众平台针对的是公众账号,除了提供管理后台之外.也开放了若干接口,让微信server和开发人员自己的应 ...

  9. [Network]Wireless and Mobile

    Wireless 1 Introduction 1.1 Elements 1. Wireless Hosts Wireless does not mean mobility. 2. Base Stat ...

  10. KFC - About KFC - Quality Assurance

    KFC - About KFC - Quality Assurance Restaurant Quality The main attributes for KFC restaurant excell ...