来自于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指向的内容还没有清空。