精华帖 (0) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-12
最后修改:2009-12-16
C++里的vector很神奇,它能够动态的增长,在拷贝的时候也是COW的。我想,可以用lazy来概括,内存分配基本都是直到用的时候才会去分配。 问题出现在。。。我是递归插入vector的,但是有要递归的为某个vector里面的元素修改他的属性 类似于 vec[parent_idx].left = build_tree(vec, a, b); 其中build_tree会返回一个线段树的root下标。 这代码看似没问题。结果一跑就挂了,而且就是在这个赋值的时候挂掉了。结果就写了一个实例程序测试一下。 #include <iostream> class Test { public: int& operator[] (int idx) { std::cout << "operator[] " << idx << std::endl; return value_; } int func() { std::cout << "func()" << std::endl; return 1; } private: int value_; }; int main(int argc, char *argv[]) { Test t; t[0] = t.func(); return 0; } 结果令我大吃一惊,在我的4.4下,竟然是先求左边的值 mike@mike-laptop% ./a.out operator[] 0 func() 不过还是不甘心,因为g++bug还是有可能的-,-.于是跑到学校的solaris上跑。上面是gcc3.4.4,结果却是反过来的。@_@ 后来经过一个在微软写编译器的大牛的点拨,才知道这是怎么回事。 这种东西叫做sequence point ambiguity。首先说什么是sequence point.imperative programming language的赋值,输出,一切一切会改变全局状态的操作叫做side effect,(这大家都应该熟悉),很明显side effect的顺序是很重要的。所以为了定义side effect的顺序,就有了sequence point,每个sequence point的前面保证了所有的side effect都能发生,每个sequence point后面的side effect都没有发生。 在C中所有的= operator都不是sequence point。所以你写一个i = i++;不会有人知道会发生什么。因为等号两边都有side effect。我个人猜测C++为了和C兼容,才造成上面的错误,由于我们把[]重载了,那么[]会发生side effect,右面的func明显也是side effect,所以上面程序的输出结果,是不确定的! 再来看我那个最开始的线段树,左面是赋值,有side effect,右面有对vector的push_back也是side effect。在gcc4.4中,左面先发生,于是vector很好的找到一个地址,给我返回,然后在执行右面,可惜,执行右面的时候内存不够了,于是vector会重新分配内存,把前面的内存free掉,然后返回了我一个值,可是这个值我是绝对没法赋给原来那个地址了,因为那地址已经失效了。相当于左边给了我一张空头支票。。。。 于是。。。。只好用一个中间变量重写了。。T_T 不过这个东西的确很隐蔽,下次一定要注意 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-12-15
试了一下,在VC++中是先计算右边的,其实这种情况可以避免的,只需要将复杂的复合语句拆分即可,如此例中:
int i = t.func(); t[0] = i; 这种难以理解,又依赖语言特性的代码,应该尽量避免. |
|
返回顶楼 | |
发表时间:2009-12-15
f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
|
|
返回顶楼 | |
发表时间:2009-12-15
最后修改:2009-12-15
Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
你确定? 求sample,我查了wiki的,wiki上是说", operator is a sequence point"的 至少g,h的顺序是能保证的。 |
|
返回顶楼 | |
发表时间:2009-12-15
dicom 写道 试了一下,在VC++中是先计算右边的,其实这种情况可以避免的,只需要将复杂的复合语句拆分即可,如此例中:
int i = t.func(); t[0] = i; 这种难以理解,又依赖语言特性的代码,应该尽量避免. 嗯 但是的确不是portable的,而且也是标准里强调的unportable的做法。 换句话说编译器爱怎么实现就怎么实现。。。 PS 你可以试试debug和release的结果是不是一样的,在Sun CC 12里,-O2和-O0的结果不一样,-O2会先左后右,-O0不会。然而,在我那个会挂掉的程序里面,却没有这种现象,由此可见Sun CC对这种东西做了优化的,结果是由优化参数而决定的。 |
|
返回顶楼 | |
发表时间:2009-12-15
最后修改:2009-12-15
Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
嗯查了一下,的确是没有规定的。 我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现 const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。 于是编辑了。。。 |
|
返回顶楼 | |
发表时间:2009-12-15
mikeandmore 写道 Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
嗯查了一下,的确是没有规定的。 我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现 const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。 于是编辑了。。。 重载 operator= 不是办法,因为它其实有两个参数,求值顺序仍然是 unspecified. |
|
返回顶楼 | |
发表时间:2009-12-15
Solstice 写道 mikeandmore 写道 Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
嗯查了一下,的确是没有规定的。 我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现 const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。 于是编辑了。。。 重载 operator= 不是办法,因为它其实有两个参数,求值顺序仍然是 unspecified. 你是说运行时的时候有两个参数? 这个倒是真的,但是标准似乎规定的是function call operator是sequence。 也就是说a.f(b)的parse顺序是b->()->a (inside f),所以()应该是在a,b之间的,所以a.f和b的顺序是确定的。 |
|
返回顶楼 | |
发表时间:2009-12-15
mikeandmore 写道 Solstice 写道 mikeandmore 写道 Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
嗯查了一下,的确是没有规定的。 我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现 const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。 于是编辑了。。。 重载 operator= 不是办法,因为它其实有两个参数,求值顺序仍然是 unspecified. 你是说运行时的时候有两个参数? 这个倒是真的,但是标准似乎规定的是function call operator是sequence。 也就是说a.f(b)的parse顺序是b->()->a (inside f),所以()应该是在a,b之间的,所以a.f和b的顺序是确定的。 a sample #include <iostream> class test { public: // const test& operator=(const test& rhs) { // return (*this); // } }; class test_run { test data; public: test& operator[](int idx) { std::cout << "[]" << std::endl; return data; } const test& func() { std::cout << "func" << std::endl; return data; } }; int main(int argc, char *argv[]) { test_run r; r[0] = r.func(); return 0; } mike@mike-laptop% ./a.out [] func uncomment以后 mike@mike-laptop% ./a.out func [] 嗯,由于我的编译器(g++4.4)的sequence point ambigious比较严重,所以还是能部分说明问题的 |
|
返回顶楼 | |
发表时间:2009-12-15
mikeandmore 写道 Solstice 写道 f(g(), h()) 中,g() 和 h() 的求值顺序也是未指明(unspecified)的,这点需要注意。
嗯查了一下,的确是没有规定的。 我的但忽然觉得我的那个解决方案还是对的。。。。-,-因为不是换成了,operator了。。。。。刚才放出来一写才发现 const test& operator=(const test& rhs)....... 明明是重载成了函数。。。。-,-不是成了参数。。。汗。。。 于是编辑了。。。 利用operator重载去解决这种问题,非常隐晦,难以理解,其实就用简单的语法语句,显式的将执行顺序定义清楚(如果执行顺序相关),简单而清晰: int i = g(); int j = h(); f(i, j); 谁能理解 f(i++, ++i, ++i, i++)这种垃圾代码? |
|
返回顶楼 | |