1.说明
[1]重载运算符函数的参数个数,应该与参与这个运算符的运算对象数量一样多,但是如果是成员函数,则参数数量要少一个,因为第一个参数是this。例如:
#include <stdio.h> #include <stdlib.h> using namespace std; class ca { public: int value; //重载为成员函数格式 int operator+(const ca &v){ return this->value + v.value; // 等同于return value+v.value; } }; //重载为非成员函数格式 int operator+(const ca &v1, const ca &v2) { return v1.value + v2.value; } int main() { ca a, b; a.value = 10; b.value = 20; printf("a+b:%d\n", a + b); // 优先用成员函数 return 0; }
[2]运算符重载函数的参数至少要有一个类的成员(或者类类型)作为参数,而不能都是内置类型(会导致编译错误)。例如int operator+(int, int)是不行的,因为int是内置类型。内置类型的操作符明显是不能重载的,比如重载了int型的+运算符,那程序其他地方的int加法都会使用重载的函数,这明显是不行的。
[3]运算符可以像普通函数一样调用。例如上面例子的a+b等同于a.operator+(b);。
[4]不应该重载的运算符(当然理论是可以重载的,只是重载之后容易出问题):逗号、&、&&、||。
[5]必须作为成员函数的重载运算符:=、[]、()、->,否则会编译错误。
[6]重载运算符函数是成员函数时,其实就是运算符左侧的对象调用的这个运算符,所以左侧对象必须是运算符所属类的一个对象(这个是当然的,因为第一个参数是这个类的this指针)。
2.输入输出运算符重载
[1]首先“<<”和“>>”表示移位操作,然后IO标准库(iostream库)重载了这两个运算符来处理输入和输出,自己重载这两个运算符的目的就是要输出自定义的类。
[2]如果要与iostream库兼容输入输出操作,则自己重载的时候必须作为非成员函数,这个是因为如果作为成员函数,则左侧的操作对象应该是这个类的对象,这样就不能兼容iostream库的输入输出操作。当然非要重载为成员函数,也是可以编译通过的,只不过是不能兼容标准库iostream的输入输出操作而已。例如:
#include <stdio.h> #include <stdlib.h> #include <string> #include <iostream> using namespace std; class ca { public: int value; //重载<<的目的就是要输出类ca ca & operator<<(const ca &v){ cout << "value:" << v.value << "\n"; return *this; } }; int main() { ca a, b, c; a.value = 1; b.value = 2; c.value = 3; //重载函数作为成员函数之后,以后只能这样调用。 //显然类ca的对象的输出已经和正常的输出不能兼容使用了。 a << a; b << a; c << b << a; //例如下面这些都是不兼容的,不能编译通过的。 (cout << "ca info :") << a; a << b << "hello.\n"; return 0; }
[3]因为IO运算符通常要访问类的所有成员,正确的做法是把重载的非成员函数声明为类的友元函数。
3.递增和递减运算符重载
[1]递增和递减一般是改变对象的状态,所以一般是重载为成员函数。
[2]重载递增递减,一定要和指针的递增递减区分开。因为这里的重载操作的是对象,而不是指针(由于指针是内置类型,指针的递增递减是无法重载的),所以一般情况的递增递减是操作对象内部的成员变量。
[3]递增和递减分为前置和后置情况,a = ++b;(前置), a = b++;(后置)。因为符号一样,所以给后置版本加一个int形参作为区分,这个形参是0,但是在函数体中是用不到的,只是为了区分前置后置。例如:
#include <stdio.h> #include <stdlib.h> #include <string> #include <iostream> using namespace std; // 例如这里的重载递增就是为了增加pos的值 class ca { public: int pos; //前置递增就是增加当前对象的pos的值,并且返回当前对象 ca operator++(){ pos++; return *this; } //后置递增就是增加当前对象的pos的值,并且返回增加pos之前的该对象 ca operator++(int){ ca ret = *this; ++*this; //这个会调用上面的函数,其实这里可以换成pos++; return ret; } }; int main() { ca a, b; a.pos = 1; b = a++; cout << "b1:" << b.pos << endl; // b1:1 b = ++a; cout << "b2:" << b.pos << endl; // b1:3 return 0; }
4.重载函数调用运算符“()”
[1]类重载了括号运算符,则该类的对象就可以当成函数一样来使用。另外类里面可以定义数据成员,这样就比普通函数多了一些功能,例如保存一些状态,统计该对象的调用次数等等。
[2]这种对象常常用作泛型算法的实参,因为有些泛型算法可以提供自定义的比较函数。例如:
#include <stdio.h> #include <stdlib.h> #include <string> #include <vector> #include <iostream> #include <algorithm> using namespace std; void myprint(const string & s) { printf("my print : %s\n", s.c_str()); return ; } class ca { public: int call_times; ca(){call_times = 0;} void operator()(const string & s){ printf("ca print : %s\n", s.c_str()); call_times++; } }; int main() { vector<string> vs; vs.push_back("abc"); vs.push_back("def"); //普通函数也是可以代替仿函数的,只是功能不如仿函数强大 for_each(vs.begin(), vs.end(), myprint); ca a; for_each(vs.begin(), vs.end(), a); //这里要注意,这个是值传递,传的是a的副本 printf("ca print call times: %d\n", a.call_times); //所以这个地方的a.call_times还是0 a("haha"); a("hehe"); printf("ca print call times: %d\n", a.call_times); //现在a.call_times的值就是2了 return 0; }