C++博客 :: 首页 ::  :: 联系 ::  :: 管理

临时对象的生存期

Posted on 2006-09-04 23:23 chenger 阅读(1354) 评论(13)  编辑 收藏 引用 所属分类: Programming Stuff
来自于CSDN上的一个帖子,题目很吓人,发现了VS 2005的一个重量级Bug!

还是直接给出代码:

#include <iostream>
#include
<string>

using
namespace std;

int
main()
{

    const
char *p = string("hello").c_str();
    cout << p << endl;

    return
0;
}


想想输出结果是什么?

这时VS2005和g++的结果就不一样了。VS2005上什么都不输出,而g++ 3.4上则输出了似乎非常合理的结果:hello,符合很多人的预期。不过查了标准以后,还是把票投给VS2005。

首先,string("hello")产生了一个temporary object,或者说临时对象。C++标准对临时对象的生存期(life time)有明确的规定,可见标准12.2节第3-5条。第3条讨论了临时对象的析构时间:

3. ... Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception.

这又涉及到full-expression的定义了,参见1.9节。整个对p的初始化构成了一个full-expression。在下结论之前,还要先看看第4、5条,分别讨论了两个例外情形,一个是将临时对象作为初始化子,例如string s = string("hello");第二是将一个引用变量绑定到这个临时对象上,例如const string &s = string("hello"),总而言之,在这两种情形中可以通过一个名字来存取这个对象,此对象的生存期就延长到变量名的作用域结束。除此之外,都按照第3条处理。

有了这些准备,拿前面给的例子往里套就明白了:这里没有出现4、5所指出的例外,因此第3条的原则适用。而不管full-expression如何,可以确定的是在p被初始化之后临时对象string("hello")的析构函数就应该被调用。在VS2005中进行调试,可以发现string析构函数调用的时间就在p被初始化之后,语句cout << p << endl执行之前。手头没有方便的工具来调试g++编译出来的程序(不太会用gdb调试C++程序,特别涉及到STL)。至于之后p指向的内存到底如何,则和具体的string实现相关了。这样分析下来,VS2005的结果还是比较不错的,而g++的结果则容易让人产生误解。

Update:察看g++编译出来的汇编代码,发现g++同样在表达式求值后析构了临时对象,只不过由于实现上的原因,p指向的内容还没有清空。

Feedback

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 17:17 by LOGOS
老实说,总感觉这种讨论意义不大,因为敢这么用string("hello").c_str()的人,应该把他给开除了....
除了给项目添乱,还能干什么

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 17:20 by shaker
就像有人讨论(a++)+(a++)+(a++)的值一样 完全不觉得这有啥意义!~

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 17:42 by chenger
弄弄清楚总是好的。而且它原来的例子更迷惑一点:

string get_str()
{
return string("hello");
}

int main()
{
const char *p = get_str().c_str();
cout << p << endl;
return 0;
}

虽然实质上是一样的。文章里的例子是我自己编的,比较矫揉造作。

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 18:05 by 周星星
“不过查了标准以后,还是把票投给VS2005”
------ 难道C++标准强制规定那部分临时对象的残余值一定不可用吗?
因为C++标准没有规定其可用,所以程序员就不应该使用,但这不是强制要求编译器一定要让使用者出错。

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 18:16 by chenger
我不是说g++错,在这个问题上g++和VC8的处理方式都没有问题,都符合标准。文中也说了,只是觉得g++的这个string实现比较容易让人误解。既然程序员不应该用,那么编译器或者库作者干脆就让其不可用不是挺好的吗?至少,出个明显的错比运行了半天然后莫名其妙地crack掉好多了

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 20:43 by 万连文
做开发的人和做学生的关注点不同,就像我以前抱住STL而不用MFC Collection一样,周星驰说过:球不是这么t的;修改一下:项目不是这么开发的。

# re: 临时对象的生存期  回复  更多评论   

2006-09-05 21:08 by chenger
我不但是学生,而且是业余……
也就是自娱自乐

# re: 临时对象的生存期  回复  更多评论   

2006-09-06 08:47 by 子弹
经常查标准的习惯不错。——呵呵

# re: 临时对象的生存期  回复  更多评论   

2006-09-09 15:28 by oosky
简单回顾了一下,g++更倾向于标准一点。并没有作明确的定义。

# re: 临时对象的生存期  回复  更多评论   

2006-09-10 12:17 by 含笑半步癫
vc2005去清空内容是多此一举。需要new的东西多了,难道要求delete清空所有new的内容?
问一下,你上面的代码是2005生成的吗?看起来很不错。

# re: 临时对象的生存期  回复  更多评论   

2006-09-10 12:28 by chenger
@含笑半步癫
最好的办法还是去看VS的stl源码,不过感觉VC STL的可读性极差……
我虽然一直在说vs2005怎么样怎么样,不过我的电脑上只装了VC++ 2005 Express,比起vs来,除了基本的ide和编译器,别的都很受限制,比如默认不带Windows SDK,也没有MFC。
我的代码都是用Vim写的。Vim里可以直接把着色的源码转换成html,然后再粘过来。唯一的问题是缩进有时候要出点问题,似乎C++博客对pre标签作了些处理,只好手工调整了。

# re: 临时对象的生存期  回复  更多评论   

2006-09-10 12:55 by 含笑半步癫
还没到需要看源码地步。
我还以为有了更好的ide了。
对了,你vc2005用release模式,看看还会不会出现你说的情况。debug模式好像有作一些操作,使调试方便。

# re: 临时对象的生存期  回复  更多评论   

2006-09-10 15:41 by chenger
都试过了,不管release还是debug,行为是一样的。
我察看了一下vc中string的实现,它使用了一个union来保存字符串,如果字符串比较小,那么就用栈上的数组来保存。在析构的时候,会重新设置字符串的最后一字节为0。

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理