More Effective C++ 条款7

原创 2001年09月28日 22:36:00

条款7:不要重载overload &&, ||, or ,. <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

C一样,C++使用布尔表达式简化求值法(short-circuit evaluation)。这表示一旦确定了布尔表达式的真假值,即使还有部分表达式没有被测试,布尔表达式也停止运算。例如:

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被减少。在这里使用逗号很方便,因为在最后一个部分里只能使用一个表达式,分开表达式来改变ij的值是不合法的。

对于内建类型&&||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[]

+    -   *   /   %   ^     &   |     ~

!    =   <   >  +=   -=   *=   /=   %=

^=  &=  |=  <<  >>   >>=  <<=  ==   !=

<=  >=  &&  ||  ++   --    ,   ->*  ->

()  []

(有关newdelete还有operator new, operator delete, operator new[], and operator delete[]的信息参见条款8

当然能重载这些操作符不是去重载的理由。操作符重载的目的是使程序更容易阅读,书写和理解,而不是用你的知识去迷惑其他人。如果你没有一个好理由重载操作符,就不要重载。在遇到&&, ||, ,时,找到一个好理由是困难的,因为无论你怎么努力,也不能让它们的行为特性与所期望的一样。

《Effective C++》:条款28-条款29

条款28避免返回handles指向对象内部成分:指的是不能返回对象内部数据/函数的引用、指针等。 条款29为异常安全而努力是值得的:指的是要有异常处理机制,避免发生异常时造成资源泄露等问题。...
  • KangRoger
  • KangRoger
  • 2015年02月19日 19:47
  • 1365

《Effective C++》:条款44-条款45

条款44将与参数无关的代码抽离templates 条款45运用成员函数模板接受所有兼容类型...
  • KangRoger
  • KangRoger
  • 2015年03月12日 22:01
  • 1475

《Effective C++》学习笔记——条款31

《Effective C++》学习笔记——条款31:将文件间的编译依存关系降至最低
  • lx417147512
  • lx417147512
  • 2015年06月15日 13:51
  • 1362

《More Effective C++》条款27:如何让类对象只在栈(堆)上分配空间?

昨天一个同学去网易面试C++研发,问到了这么一个问题:如何限制一个类对象只在栈(堆)上分配空间? 一般情况下,编写一个类,是可以在栈或者堆分配空间。但有些时候,你想编写一个只能在栈或者只能在堆上面分...
  • hxz_qlh
  • hxz_qlh
  • 2013年10月26日 21:27
  • 6031

more effective c++使用条款(一)—— 基础议题/运算符

第一章:基础议题   Item M1:指针与引用的区别     1.指针可以不初始化,引用必须初始化     2.指针可以为空指针,引用不可以为空     3.指针可以改变指向,引用初始化...
  • weixin_36381867
  • weixin_36381867
  • 2017年05月06日 20:05
  • 122

[More Effective C++]条款22有关返回值优化的验证结果

转自:http://blog.sina.com.cn/s/blog_5dbb2c470100xapn.html 不知结果是否正确,有待进一步确认 (这里的验证结果是针对返回值优化的,其实和条款22本...
  • simanstar
  • simanstar
  • 2016年10月10日 17:24
  • 232

《More Effective C++》条款26:限制某个Class所能产生的对象数量

问题来源:假设系统中只有一台打印机,如何把打印机对象数止限定为一个。或者系统只提供16个可分发出去的文件描述符,如何确保文件描述符对象存在的数目不能超过16个。 在具体的应用过程中,我们可能需要限制...
  • hxz_qlh
  • hxz_qlh
  • 2013年11月04日 21:15
  • 901

[More Effective C++]条款十九:理解临时对象的来源

条款十九:理解临时对象的来源   当程序员之间进行交谈时,他们经常把仅仅需要一小段时间的变量称为临时变量。例如在下面这段swap(交换)例程里:   template   void swap...
  • eric491179912
  • eric491179912
  • 2011年08月30日 17:29
  • 561

《More Effective C++》条款27:如何让类对象只在栈(堆)上分配空间?

昨天一个同学去网易面试C++研发,问到了这么一个问题:如何限制一个类对象只在栈(堆)上分配空间? 一般情况下,编写一个类,是可以在栈或者堆分配空间。但有些时候,你想编写一个只能在栈或者只能在堆上...
  • qq1010234991
  • qq1010234991
  • 2016年05月23日 10:03
  • 131

more effective c++条款总结

条款一:仔细区别 pointer(指针)和references(引用)。 条款二:最好使用c++类型的转型操作符。 条款三:绝不要以多态方式处理数组。 条款四:非必要,不要提供无参构造。 条款...
  • qq_37185716
  • qq_37185716
  • 2017年07月25日 16:08
  • 113
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:More Effective C++ 条款7
举报原因:
原因补充:

(最多只允许输入30个字)