tag 标签: 重载操作符

相关博文
  • 热度 8
    2016-4-25 23:28
    1101 次阅读|
    0 个评论
    果然进入Part III了之后,难度也不一样了。 copy control实际上没怎么看懂,过段时间还得回头加深理解。 Anyway,现在继续往前学习,进入重载操作与转换式: Overloaded operations and Conversions 14.1 Basic Concepts ---------------------------------------------------------------- 重载的操作符有特殊的名称:关键字operator,跟随操作符的symbol。 重载操作符的函数,和所以其他函数一样,具备返回值、参数列表和函数体。 另外,函数调用的操作符“()”,也可以被重载。 如果重载操作符的函数是类的成员函数的话,左操作数隐式的绑定在this指针上。 大多数的操作符都可以被重载,除了以下几种: ::  .*  .  ?: 我们不能重载build-in类型的操作,也不能发明新的操作符。 重载过的操作符的优先级和结合性,与重载之前相同。 比如下面的代码: class Foo { public:     Foo() = default;     Foo operator+(const Foo);     int i; private:     string *ps; }; Foo Foo::operator+(const Foo rhs) {     i += rhs.i;     return *this; } int main(int argc, char *argv ,(),-应该被重载为成员函数。  * 混合赋值的函数,一般应该重载为成员函数。  * 和对象的state密切相关的,或者和类type绑定(++,--,*)的操作符应该重载为成员函数。  * sysmmetric操作符,比如算术、相等、关系、bitwise操作符,应该重载为普通函数。 string类就将“+”重载成了非成员函数,因此下面两个表达式都合法: string u1 = "Hi" + s; string u2 = s + "Hi"; 14.2 Input and Output Operators ---------------------------------------------------------------- 包含IO操作的类,一般都会定义input和output操作。 output操作符的第一个参数通常是ostream对象的nonconst引用(因为需要更改ostream状态); output操作符的第二个参数通常是类的const引用(因为要避免copy操作)。 input和output操作符必须重载为非成员函数; 由于它们对私有成员的访问,因此通常将它们声明为friends。 input操作符通常得处理出错的情况,output操作符则不担心这个问题。 什么时候input操作会出错:  * stream的数据类型出错。  * 读取到了end-of-file,或者其他istream错误。 14.3 Arithmetic and Relational Operators ---------------------------------------------------------------- 一般来说,如果一个类定义了算术操作和related compound assignment操作, 那么通常应该使用后者来实现前者。 一般来说,如果定义了“==”,那么“”最好和它的物理含义兼容, 因此其实有些类,我们并不需要为它定义“”操作。 14.4 Assignment Operators ---------------------------------------------------------------- 它可以被重载,它必须被定义为成员函数,它应该是copy-assigning、move-assigning兼容。 14.5 Subcript Operators ---------------------------------------------------------------- 下标操作符必须重载为成员函数。 14.6 Increment and Decrement Operators ---------------------------------------------------------------- 这两个操作符,对于迭代器来说非常常见。 一般来说,建议将它们实现为成员函数。 一般来说,prefix和postfix应该一起实现。 比如下面的代码: Foo operator++(); Foo operator--(); Foo operator++(int); Foo operator--(int); 14.7 Member Access Operators ---------------------------------------------------------------- “-”,必须被重载为成员函数; “*”,一般需要被重载为成员函数。 ( * 这一节很有难度,需要以后再深入的学习!!!) 14.8 Function-Call Operators ---------------------------------------------------------------- 函数调用的操作符“()”,也可以被重载。 比如下面的代码: struct absInt {     int operator()(int val) const {         return (val 0)? -val : val;     } }; int main(int argc, char *argv ) {     vectorstring vs = {"123", "456", "789", "abc", "defg"};     for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));     return 0; } 也就是说,作为算法的函数参数,可以是lambda表达式、bind函数,以及函数对象类。 实际上,lambda表达式就是没有命名的函数对象。 库里面定义了一些操作的模板,比如plus: plusint intAdd; int sum = intAdd(10, 20); cout sum endl; 输出结果是: $ ./hello2 30 库函数对象,也可以和算法结合起来使用。 比如下面的代码: vectorstring vs = {"123", "456", "789", "abc", "defg"}; for_each(vs.begin(), vs.end(), PrintString(cout, ' ')); cout endl; sort(vs.begin(), vs.end(), greaterstring()); for_each(vs.begin(), vs.end(), PrintString(cout, ' ')); cout endl; 输出结果是: $ ./hello2 123 456 789 abc defg defg abc 789 456 123 库函数对象可以直接作用于指针,这样我们可以比较地址的高低(直接比较的话,某些情况下无意义)。 另外,function模板可以用来将函数指针、函数对象、lambda表达式都转换成指定的函数类型: functionint(int, int) f1 = add;     //函数指针 functionint(int, int) f2 = dev();   //函数对象 functionint(int, int) f3 = [](int i, int j) {return i * j}  //lambda表达式 这样,可以方便的用函数名称key定义函数类型的map: mapstring, int(*)(int, int) binops; 或者: mapstring, functionint(int, int) binops; 14.9 Overloading, Conversions, and Operators ---------------------------------------------------------------- 转换操作符(Conversion Operators): operator type() const; 比如: operator int() const { return val; } 它必须是类的成员函数,不能有返回值,参数表必须为空,并且总是const的。 转换操作符总是隐式的发生,因此无法为它传递参数。 转换操作符虽然没有返回值,但是它必须返回一个type类型的值。 慎用类的类型转换!