1,操作符重载
跟函数重载意思一样,就是把操作符重新定义一遍,让它能适应特别的操作,比如类的加减乘除啊、输入输出什么的,好处是在使用的时候更坚定,一个+、-写出来就可以让两个类进行操作,看起来也比较符合习惯。
C++++重要规则:只要没有歧义,函数能唯一解释,你想怎样编程就怎样编程,包括重载、省略、简化!
声明是这样子:
Time operator+(const Time & t) const;
在class.h里面public部分声明,Time是类名称,operator+表明操作符,+是操作符,后面跟函数就一模一样了。
然后是函数定义:
Time Time::operator+(const Time & t) const
{
Time sum;
sum.x = x+t.x;
sum.y = y+t.y;
return sum;
}
函数里面如何定义跟普通函数重载没区别的。
调用:
Time total, add1, add2;
total = add1.operator+(add2);
这样子看出来了吧,operator+是类函数名字,operator+()就是add1这个类的类函数,参数是add2。
现在看操作符重载的优势:.operator可以省略掉!!!
total = add1+add2;
这样看起来多么直观,跟普通的常数加减一样,但是执行的时候调用operator+()这个类函数。
根据加法的交换律:
total = add2+add1;
但是这时候operator()变成了add2的类函数,虽然结果与原来一样。
2,友元函数
先看例子
声明:Time operator*(double mult) const;
定义:
Time Time::operator*(double mult)const
{
Time result;
result.x = x*mult;
result.y = y*mult;
return result;
}
这还是操作符*重载,和+、-这种没区别。
调用的时候:
Time result, mult1; double m;
result = mult1.operator*(m);
把.operator省略掉:
result = mult1*m;
我们知道乘法也有交换律:result = m*mult1;
但是现在问题来了,我们知道,这样写是因为吧.operator省略了,现在把它补回去,发现不行了,因为m不是Time类,只是个double类型,没有类函数。为了解决这个问题,C++++引入了友元函数。
friend Time operator*(double m, const Time & t);
这是声明部分,在类的public中声明,但是前面加了friend,表明这不是个类函数,不是类函数有什么好处呢,待会儿解答。
定义:
Time operator*(double m, const Time & t)
{
Time result;
result.x = m*t.x;
result.y = m*t.y;
return result;
}
跟类函数的区别是,函数名operator*之前没有类名Time::,表明友元函数不是类函数。但是因为它是在类中声明的,所以友元函数可以使用类中的private对象:比如t.x、t.y。这是别的函数不具备的功能,正是友元函数特殊之处。
这时候再来看
result = m*mult1;
正常调用应该是result = operator*(m, mult1); 但是现在我们是操作符重载,整这么复杂,当然要有些优势啦,所以可以写成:
result = m*mult1;
这样,碰到操作符*的情况
result = mult1*m;翻译成:result = mult1.operator*(m);
result = m*mult1;翻译成:result = operator*(m, mult1);
之所以这样子,是因为*是二元操作符,默认把左右两边的参数都当成自己的参数。
也许有人会问,可不可以定义一个这样的类函数:
Time operator*(double m, const Time & t) const;
把m, 和t都当参数调用,然后调用的时候:
result = m*mult1;
这个没法解释,因为m, mult1都是参数,operator*()这个类函数的类对象没了。并且编译的时候,编译器会提示你operator*是个二元操作符,而你传入了三个参数,以为它默认类对象是第一个参数。
而友元函数没有这问题,因为它不是类函数,所以就没有类对象,所以不会多出一个参数。
3,<<、>>重载
通过对<<重载,可以直接输出类中的内容,cout << mult1; 比较直观,使用的也是友元函数。
friend std::ostream & operator<<(std::ostream & os, const Time & t);
为什么不用类函数?因为使用了类函数的话,调用的时候:
mult1.operator<<(cout);
省略掉.operator就变成了:mult1 << cout;
看起来很奇怪,使用友元函数,第一个参数用std::ostream, 第二个参数用类,这样就看起来比较正常了。
为什么返回类型是 std::ostream & ? 这是为了兼容:
cout << mult1 << mult2 << mult3;
因为根据cout 的调用顺序从左向右,每次返回的都是cout本身,而不是副本。
>>重载基本一样,需要注意的是>>类型是输入std::istream。
4,类的自动转换
定义类
class Stonewt
{
private:
int stone;
double pds_left;
double pounds;
public:
Stonewt(double lbs);// #1
Stonewt(int stn, double lbs);// #2
Stonewt();// #3
Stonewt operator+(const Time & t) const;
Stonewt operator-(const Time & t) const;
Stonewt operator*(const Time & t) const;
};
我们知道,类的初始化可以用:
Stonewt stone1=Stonewt(5); // #1
Stonewt stone2(5); // #1
Stonewt stone3=Stonewt(5, 2); // #2
Stonewt stone4(5, 2); // #2
Stonewt stone5; // #3
但是可不可以这样呢?
Stonewt stone6=5;
或者这样:
Stonewt stone7;
stone7 = 5;
答案是可以的!
这个有什么用呢?
看以下调用:
Stonewt result;
result = stone1+stone2; // 直接调用 stone1.operator+(stone2);
result = stone1+5; // stone1.operator+(5),由于operator+()函数的参数是Stonewt类型,这里会直接调用Stonewt(5),以符合函数类型。
再看一个:
result = 5+stone1;
这个要怎么解释?先把5转换为Stonewt(5)再使用operator+()???但是,编译器不是这么想的。因为类函数中有operator+这个二元操作符,而这里5和stone1刚好是两个参数,所以它会把直接带入,然后发现其需要的两个参数都是Stonewt类型,而这里一个int一个Stonewt类型,不符合要求。
这里需要友元函数进行解释:
friend Stonewt operator*(int m, const Stonewt & s) {return s*m};
friend Stonewt operator+(int m, const Stonewt & s) {return s+m};
friend Stonewt operator-(int m, const Stonewt & s) {return Stonewt(m)-t};