4-1 运算符
1 运算符的重载
其实从一开始我们就已经见过运算符的重载了,例如,运算符*
用于地址,就可以得到存储在这个地址中的值;用在两个数字上时,得到的是乘积的效果。
重载运算符需要使用被称作运算符函数的特殊函数形式,其格式如下
operator op(argument-list)
则operator +()
重载+运算符,operator *()
重载*运算符。
通过一个重载的示例来了解,现有一个Box
类,为它定义一个operate+()
成员函数来重载+运算符,以便实现两个对象的长宽相加。
class Box{
public:
int length;
int width;
Box operator+(const Box& box1){
Box box;
box.length=this->length+box1.length;
box.width=this->width+box1.width;
return box;
}
};
如果box2
和box3
、sum
都是类对象,就可以编写这样的等式:
sum=box2+box3;
//编译器行为
sum=box2.operator+(box3);
可见重载的目的就在于改变其内部操作的效果,但要注意像运算符优先级这种是无法改变的。
2关联性与优先级
在一般表达式中先运行优先级高的运算符,再运行优先级低的运算符,比如a+b*c
先运行乘法再加法,但我们可以用()
将a+b关联起来先运行。运算符的优先级有很长一串,很多时候不可能完全记住,C++Primer在这一方面的建议是:当你拿捏不准时,通通用括号来实现你想要的运算顺序
3 注意事项
1.不建议在同一个表达式中改变它的值还同时引用它,这样做不仅使代码的可读性变差,而且容易出错。
比如
int i=0;
cout<<i<<" "<<++i;
编译器会发出警告Unsequenced modification and access to 'i'
,因为它不知道是该先输出还是先进行++的操作。
为了增加我们对这一点的印象,再观察这样一个示例:
string s="helloworld";
auto beg = s.begin();
while(beg!=s.end()){
*beg = toupper(*beg++);
}
在这个大小写转换的代码中,我们希望先进行*beg = toupper(*beg)
,再进行beg++
的操作。但是由于我们在同一句话里使用且改变beg
,编译器很有可能会先进行beg++
的操作,再进行*beg = toupper(*beg)
。
- ||、&&
对于这两个操作符有一个特别的点就是,如果前半部分已经满足/不满足条件,那么操作符后面的部分将不再进行判断,比如对于a||b
,如果已经知道a的值判断为1,那么无论b是0还是1都不会对最终的结果产生影响,同理对于a&&b
,如果已经知道a的值判断为0,那么b无论是1还是0都对最终的结果没有影响。
- 执行顺序
编译器可以确保表达式中的运算优先级,但是不会保证它的执行顺序,比如
g()+f()*m()-n();
在这样的式子里编译器不会确保先调用哪个函数,如果在这几个函数里有些有关联的话,比如说m()
函数依赖于g()
函数得到的某个值,那么建议最好分开写,在获得每一个函数的返回值后再进行最终的运算。