我有一个方法,它返回一个字符串以显示为错误消息。根据程序中出现此错误的位置,我可能会在显示错误消息之前添加一些解释。
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
提供额外参数会使其按预期工作?
答案 0 :(得分:12)
("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,这将导致构造函数和复制构造函数(&lt; C ++ 11,非常糟糕< / strong>)或移动构造函数(&gt; = C ++ 11,更好,但不必要)执行。
顺便说一句,“C ++编程语言”4 th 版本甚至涵盖了这个确切的问题!在§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- 由c_str()
返回的样式字符串被分配为临时字符串的一部分 对象持有s1+s2
,并且不保证存储后存储 那个临时的被毁了。因此,cs
指向已取消分配 存储。输出操作cout<<cs
可能会按预期工作,但是 这将是纯粹的运气。编译器可以检测并发出警告 这个问题的很多变种。if
语句的问题有点微妙。该 条件将按预期工作,因为其中的完整表达式 创建临时保留s2+s3
是条件本身。 但是,该临时值在受控声明之前被销毁 已输入,因此无法保证cs
的使用。
所以,不要担心你的C ++技能。甚至C ++圣经也解释了它。 ; - )
答案 1 :(得分: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 :(得分: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();