
More Effective C++:不要重载的操作符
发布时间:2006-09-19 12:52:37 来源:csdn blog 网友评论 0 条
与C一样,C++使用布尔表达式简化求值法(short-circuit evaluation)。这表示一旦确定了布尔表达式的真假值,即使还有部分表达式没有被测试,布尔表达式也停止运算。例如:
这里不用担心当p为空时strlen无法正确运行,因为如果p不等于0的测试失败,strlen不会被调用。同样:
如果index小于lowerBound,它不会与upperBound进行比较。
很早以前上述行为特性就被反复灌输给C和C++的程序员,所以他们都知道该特性。而且他们也依赖于简短求值法来写程序。例如在上述第一个代码中,当p为空指针时确保strlen不会被调用是很重要的,因为C++标准说(正如C标准所说)用空指针调用strlen,结果不确定。
C++允许根据用户定义的类型,来定制&&和||操作符。方法是重载函数operator&& 和operator||,你能在全局重载或每个类里重载。然而如果你想使用这种方法,你必须知道你正在极大地改变游戏规则。因为你以函数调用法替代了简短计算法。也就是说如果你重载了操作符&&,对于你来说代码是这样的:
对于编译器来说,等同于下面代码之一:
这好像没有什么不同,但是函数调用法与简短求值法是绝对不同的。首先当函数被调用时,需要运算其所有参数,所以调用函数functions operator&& 和 operator||时,两个参数都需要计算,换言之,没有采用简短计算法。第二是C++语言规范没有定义函数参数的计算顺序,所以没有办法知道表达式1与表达式2哪一个先计算。完全与具有从左参数到右参数计算顺序的简短计算法相反。
因此如果你重载&&或||,就没有办法提供给程序员他们所期望和使用的行为特性,所以不要重载&&和||。
同样的理由也适用于括号操作符,但是在我们深入研究它之前,我还是暂停一下,让你不要太惊讶,“逗号操作符?哪有逗号操作符?”确实存在。
逗号操作符用于组成表达式,你经常在for循环的更新部分(update part)里遇见它。例如下面来源于Kernighan's and Ritchie's 经典书籍The C Programming Language 第二版(Prentice-Hall, 1988)的函数:
在for循环的最后一个部分里,i被增加同时j被减少。在这里使用逗号很方便,因为在最后一个部分里只能使用一个表达式,分开表达式来改变i和j的值是不合法的。
对于内建类型&&和||,C++有一些规则来定义它们如何运算。与此相同,也有规则来定义逗号操作符的计算方法。一个包含逗号的表达式首先计算逗号左边的表达式,然后计算逗号右边的表达式;整个表达式的结果是逗号右边表达式的值。所以在上述循环的最后部分里,编译器首先计算++i,然后是—j,逗号表达式的结果是--j。
也许你想为什么你需要知道这些内容呢?因为你需要模仿这个行为特性,如果你想大胆地写自己的逗号操作符函数。不幸的是你无法模仿。
如果你写一个非成员函数operator,你不能保证左边的表达式先于右边的表达式计算,因为函数(operator)调用时两个表达式做为参数被传递出去。但是你不能控制函数参数的计算顺序。所以非成员函数的方法绝对不行。
剩下的只有写成员函数operator的可能性了。即使这里你也不能依靠于逗号左边表达式先被计算的行为特性,因为编译器不一定必须按此方法去计算。因此你不能重载逗号操作符,保证它的行为特性与其被料想的一样。重载它是完全轻率的行为。
你可能正在想这个重载恶梦究竟有没有完。毕竟如果你能重载逗号操作符,你还有什么不能重载的呢?正如显示的,存在一些限制,你不能重载下面的操作符:
你能重载:
当然能重载这些操作符不是去重载的理由。操作符重载的目的是使程序更容易阅读,书写和理解,而不是用你的知识去迷惑其他人。如果你没有一个好理由重载操作符,就不要重载。在遇到&&, ||, 和 ,时,找到一个好理由是困难的,因为无论你怎么努力,也不能让它们的行为特性与所期望的一样。
| char *p; ... if ((p != 0) && (strlen(p) > 10)) ... |
这里不用担心当p为空时strlen无法正确运行,因为如果p不等于0的测试失败,strlen不会被调用。同样:
| int rangeCheck(int index) { if ((index < lowerBound) || (index > upperBound)) ... ... } |
如果index小于lowerBound,它不会与upperBound进行比较。
很早以前上述行为特性就被反复灌输给C和C++的程序员,所以他们都知道该特性。而且他们也依赖于简短求值法来写程序。例如在上述第一个代码中,当p为空指针时确保strlen不会被调用是很重要的,因为C++标准说(正如C标准所说)用空指针调用strlen,结果不确定。
C++允许根据用户定义的类型,来定制&&和||操作符。方法是重载函数operator&& 和operator||,你能在全局重载或每个类里重载。然而如果你想使用这种方法,你必须知道你正在极大地改变游戏规则。因为你以函数调用法替代了简短计算法。也就是说如果你重载了操作符&&,对于你来说代码是这样的:
| if (expression1 && expression2) ... |
对于编译器来说,等同于下面代码之一:
| if (expression1.operator&&(expression2)) ... // when operator&& is a // member function if (operator&&(expression1, expression2)) ... // when operator&& is a // global function |
这好像没有什么不同,但是函数调用法与简短求值法是绝对不同的。首先当函数被调用时,需要运算其所有参数,所以调用函数functions operator&& 和 operator||时,两个参数都需要计算,换言之,没有采用简短计算法。第二是C++语言规范没有定义函数参数的计算顺序,所以没有办法知道表达式1与表达式2哪一个先计算。完全与具有从左参数到右参数计算顺序的简短计算法相反。
因此如果你重载&&或||,就没有办法提供给程序员他们所期望和使用的行为特性,所以不要重载&&和||。
同样的理由也适用于括号操作符,但是在我们深入研究它之前,我还是暂停一下,让你不要太惊讶,“逗号操作符?哪有逗号操作符?”确实存在。
逗号操作符用于组成表达式,你经常在for循环的更新部分(update part)里遇见它。例如下面来源于Kernighan's and Ritchie's 经典书籍The C Programming Language 第二版(Prentice-Hall, 1988)的函数:
| // reverse string s in place void reverse(char s[]) { for (int i = 0, j = strlen(s)-1;i < j;++i, --j) // 啊! 逗号操作符! { int c = s[i]; s[i] = s[j]; s[j] = c; } } |
在for循环的最后一个部分里,i被增加同时j被减少。在这里使用逗号很方便,因为在最后一个部分里只能使用一个表达式,分开表达式来改变i和j的值是不合法的。
对于内建类型&&和||,C++有一些规则来定义它们如何运算。与此相同,也有规则来定义逗号操作符的计算方法。一个包含逗号的表达式首先计算逗号左边的表达式,然后计算逗号右边的表达式;整个表达式的结果是逗号右边表达式的值。所以在上述循环的最后部分里,编译器首先计算++i,然后是—j,逗号表达式的结果是--j。
也许你想为什么你需要知道这些内容呢?因为你需要模仿这个行为特性,如果你想大胆地写自己的逗号操作符函数。不幸的是你无法模仿。
如果你写一个非成员函数operator,你不能保证左边的表达式先于右边的表达式计算,因为函数(operator)调用时两个表达式做为参数被传递出去。但是你不能控制函数参数的计算顺序。所以非成员函数的方法绝对不行。
剩下的只有写成员函数operator的可能性了。即使这里你也不能依靠于逗号左边表达式先被计算的行为特性,因为编译器不一定必须按此方法去计算。因此你不能重载逗号操作符,保证它的行为特性与其被料想的一样。重载它是完全轻率的行为。
你可能正在想这个重载恶梦究竟有没有完。毕竟如果你能重载逗号操作符,你还有什么不能重载的呢?正如显示的,存在一些限制,你不能重载下面的操作符:
| . .* :: ?: new delete sizeof typeid static_cast dynamic_cast const_cast reinterpret_cast |
你能重载:
| operator new operator delete operator new[] operator delete[] + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () [] |
当然能重载这些操作符不是去重载的理由。操作符重载的目的是使程序更容易阅读,书写和理解,而不是用你的知识去迷惑其他人。如果你没有一个好理由重载操作符,就不要重载。在遇到&&, ||, 和 ,时,找到一个好理由是困难的,因为无论你怎么努力,也不能让它们的行为特性与所期望的一样。
全站资源
- 微软官方入门教程19:轻松掌握Vista系统的快
- 微软2008大冲击,预借Vista SP1力促Vista市
- 在收件箱中获得 Windows Vista 的最新更新
- 微软官方Vista入门教程全集19篇(Vista学院
- Windows Vista 的成功将势不可挡
- 快快抛弃Vista,拥抱XP SP3!你觉得呢?
- 浅谈Vista系统关闭虚拟内存与使用内存盘加速
- 嘿嘿,按下键盘上面的三个键,马上让你的Vi
- Windows Vista的盗版率只有Windows XP的一半
- 3DMark和PCMark Vantage新版将只支持Vista系
阅读排行
- Borland 发布C++ Builder 2006 RAD 环境
- C/C++程序员应聘常见面试题深入剖析
- Visual C++常用数据类型转换详解
- C++中的 static 关键字
- 利用VC++实现局域网实时视频传输
- 浅谈C/C++内存泄漏及其检测工具
- 英国投票否决C++/CLI 微软强攻ISO标准受挫
- VC++下用MSComm控件实现串口通讯
- 伪随机数生成及在VC++中的实现
- VC++编程实现对波形数据的频谱分析
最新技术文档
- C++中用赋值形式op=取代单独形式op
- C++启蒙之用C++制作程序
- C++启蒙之C++中的类
- 解析C++/CLI之头文件、内联函数与数组
- C++启蒙之控制结构
- C++启蒙之注释符号
- 了解C++异常处理的系统开支
- C++语言学习之从结构到类
- C++中理解“传递参数”和异常之间的差异
- C++中禁止异常信息传递到析构函数外
专题教程
- 大话G游 专题:手机病毒揭密
- ARP攻击防范与解决方案 路由故障处理手册
- Picasa中文版_Picasa教程 专题:清除流氓软件
- Firefox专题 seo搜索引擎优化专区
- 重装Windows必知的事情 装机之必备软件大行动
病毒专杀栏
