getString()
返回 str
的一个副本(getString()
通过值返回);
是的,没错。
因此,在 main()
中,str
的副本将在 main()
返回之前一直保持存活状态。
不是的,返回的副本是一个临时的 std::string
,它将在创建它的语句结束时被销毁,即在 std::cout << cStr << std::endl;
之前。然后,cStr
就会变成悬空指针,对其进行解引用将导致未定义行为,任何事情都有可能发生。
你可以将返回的临时对象复制到一个命名变量中,或将其绑定到一个 const
左值引用或右值引用上(临时对象的生命周期将延长到引用离开作用域)。例如:
std::string s1 = getString();
const char* cStr1 = s1.c_str();
std::cout << cStr1 << std::endl;
const std::string& s2 = getString();
const char* cStr2 = s2.c_str();
std::cout << cStr2 << std::endl;
std::string&& s3 = getString();
const char* cStr3 = s3.c_str();
std::cout << cStr3 << std::endl;
或者在临时变量被销毁之前使用指针。例如:
std::cout << getString().c_str() << std::endl; // temporary gets destroyed after the full expression
以下内容来自[The.C++.Programming.Language.Special.Edition] 10.4.10 Temporary Objects [class.temp]:
除非与引用绑定或用于初始化命名对象,否则临时对象将在创建它的完整表达式结束时被销毁。完整表达式是不是某些其他表达式的子表达式的表达式。
标准字符串类有一个成员函数c_str(),返回以C风格'\0'结尾的字符数组(§3.5.1,§20.4.1)。此外,运算符+被定义为字符串连接。这些是非常有用的字符串功能。然而,它们组合使用可能会导致晦涩难懂的问题。
例如:
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´) {
}
}
也许,你的第一反应是“但不要这样做”,我同意。然而,这种代码确实会被编写,因此值得知道它是如何被解释的。
一个临时的string类对象被创建来保存s1 + s2。接下来,从该对象中提取了一个指向C风格字符串的指针。然后,在表达式结束时,临时对象被删除。现在,C风格字符串被分配到哪里了呢?可能是作为保存s1 + s2的临时对象的一部分,但该存储空间不能保证在该临时对象被销毁后仍然存在。因此,cs指向已释放的存储空间。输出操作cout << cs可能会按预期工作,但那只是纯粹的运气。编译器可以检测并警告对这个问题的许多变体。
main()
返回? - David Schwartz