2015-10-20 148 views
3

奇怪的std :: cout行为我有一个方法返回一个字符串显示为错误消息。根据程序中发生此错误的位置,我可能会在显示错误消息之前添加更多解释。与const char *

string errorMessage() { 
    return "this is an error"; 
} 

// somewhere in the program... 
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << message << endl; 

(我存储该消息作为一个const char *因为我将实际提供该误差,其接受为const char *参数的另一种方法)

在这一点上,输出垃圾(不可打印的字符的安慰)。

所以我打了一下,发现如果不是我做的:

// somewhere in the program... 
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << ("Some extra info \n" + errorMessage()).c_str() << endl << message << endl; 

则显示消息正确的两倍。

为什么提供cout的额外参数会导致它按照我的意图工作?

回答

11

("Some extra info \n" + errorMessage())临时std::string。这意味着,声明完成后,它的寿命已经结束。

cout << ("Some extra info \n" + errorMessage()).c_str() << endl 

作品,因为在该点std::cout使用std::string其生命周期尚未结束。

<< message 

部分是未定义的行为,虽然。纯粹的运气它的工作原理。

要解决此问题,需要将std::string的一生与无论是const std::string&延长或,因为C++ 11,std::string&&

const std::string& str_const_ref = "Some extra info \n" + errorMessage(); 
std::string&& str_rvalue = "Some extra info \n" + errorMessage(); 

现在你可以在他们为你想操作。

另一种方式是

std::string str = "Some extra info \n" + errorMessage(); 

但是,如果编译器不会做一些Return Value Optimization,这将导致一个构造拷贝构造函数(< C++ 11,非常糟糕 )或移动构造函数(> = C++ 11,更好,但不必要)得到执行。


顺便说一句,这个确切问题,甚至覆盖 “的C++程序设计语言” 4 版!

在§10.3.4“临时对象”,Stroustrup的先生写道:

标准库串具有构件c_str()(§36.3),它返回一个C语言风格的指针到零封端阵列字符 (§2.2.5,§43.4)。另外,运算符+被定义为表示字符串 级联。这些是字符串的有用设施。但是,在 组合它们可能会导致模糊的问题。例如:

void f(string& s1, string& s2, string& s3) { 
    const char* cs = (s1+s2).c_str(); 
    cout << cs; 
    if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') { 
     // cs used here 
    } 
} 

[...]甲临时字符串对象被创建来保存s1+s2。接下来,从该对象提取指向C风格字符串的指针 。然后 - 在 的末尾表达式 - 临时对象被删除。然而,由c_str()返回的样式字符串被分配作为临时 对象的一部分,持有s1+s2,并且该存储不保证在 之后临时销毁。因此,cs指向释放 存储。输出操作cout<<cs可能会按预期工作,但 这将是纯粹的运气[1]。编译器可以检测并警告这个问题的很多变体。 if -statement的问题有点微妙。 条件将按预期工作,因为创建临时存储s2+s3的完整表达式是条件本身。 但是,在输入受控语句 之前,该临时销毁已被销毁,因此任何使用cs都无法保证正常工作。

所以,不要担心你的C++技能。即使是C++圣经也解释它。 ;-)

+0

啊,这是有道理的

我建议这样做,而不是。这是事实,它总是在一个案件中工作,而从来没有在其他案件中真正投掷我 – rbennett485

+0

@ rbennett485这样的事情几乎总是未定义的行为或错误的宏观。 :-) – Downvoter

+0

当你说'这样的事情'时,你的意思是一个程序,有时按预期工作,有时不会? – rbennett485

3
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << message << endl; 

errorMessage()返回一个临时std::string对象
"Some extra info \n" + errorMessage()级联创建另一个临时对象。
取其c_str它返回一个指向其内部缓冲区(不是副本)的指针。
然后临时对象被删除,并且指针无效。
其他一切都是未定义的。它可能会给出正确的输出,崩溃或做其他事情。

2

的问题是在这里:

const char* message = ("Some extra info \n" + errorMessage()).c_str(); 

的errorMessage()将返回一个临时的std :: string将走出去的范围下一行运行之前。当你需要一个指针传递给底层的缓冲区,你可以使用

std::string message = "Some extra info \n" + errorMessage(); 

然后:

message.c_str();