前注: 本文除了最后半句话在讨论“i++”的问题外,通篇都不关i++或++i什么事。千万别望文生义。
王富涛 的一个话题,问:
- int i = 0;
- cout << i << " " << i++ << endl;
为什么在VC6里,DEBUG 版本输出 1,0 ,而Release版本输出0,0。
通常编译器,都是编译Release程序时,容易出错,因为错误的优化。不过VC的习惯是,通常RELEASE版运行的结果,更接近C++标准,私下里我们称它的优化,就是让程序更“标准化”。
这边不准备纠缠这个问题。倒是来看看,cout << i << " " << i++ << endl 是些什么东东?其它上面的代码相当于:
- int i=0;
- ::operator << (cout.operator << (i), " ").operator << (i++).operator << (endl);
为什么这么复杂?是因为凑巧中间夹着输出了一个字符串,此时调用的是自由函数 ::operator(ostream&, char const *);
还是有些眼晕,那么我们再简化一下:
- #define OUT operator<<
- ::OUT(cout.OUT(i), " ").OUT(i++).OUT(endl);
- #undef OUT
为了描述方便,干脆为每个OUT编个号码:
- ::OUT_1(cout.OUT_2(i), " ").OUT_3(i++).OUT_4(endl);
其中,OUT_1 是一个全局自由函数,它带两个参数,一个是流,另一个是字符串,大致原型可以写成:
- ostream& OUT_1(ostream& os, char const * pstr);
而 OUT_2 ~ OUT_4, 都是同一个函数,它们是某个类的成员函数。
cout 就是那个类的一个对象(换句话说:cout一个变量)。
endl 本是一个函数 endl()……但这里用的是它的地址,这是另一个话题了。
忽略与本问题无关的,最终代码结构类似:
- foo_1( o.foo_2(i), " ").foo_3(i++).foo_4(endl);
foo_1() 函数运行之后,返回一个对象,假设为a。接着,调用a的成员函数foo_3,拆解代码为:
- a = foo_1 (...);
- a.foo_3 (i++);
- //...
但要执行foo_1,则必须求出它的第一个入参的值,所以再拆解:
- a_0 = o.foo_2(i);
- a_1 = foo_1(a_0, " ");
- a_2 = a_1.foo_3(i++);
- a_3 = a_2.foo_4(endl);
补充一句,a_x 在本例中,其实都是同一个对象。
不管如何,在执行 a_0 = o.foo_2(i)时,关后面的 a_1.foo_3(i++)什么事呢?那时候 foo_3()的宿主a_1还没有产生呢,如果有编译器非在这时让i++影响了前面的o.foo_2(i)的执行,造成后者输出一个“1”来,那能说明什么呢?我想,只能说是它错了吧。