我有以下代码:
std::string getString() {
std::string str("hello");
return str;
}
int main() {
const char* cStr = getString().c_str();
std::cout << cStr << std::endl; // this prints garbage
}
我以为会发生的是,getString()
将返回一个复制的str
(getString()
按价值计算收益); 因此,的副本str
将保持“活动”状态main()
直到main()
返回。这将cStr
指向一个有效的内存位置:所返回的副本的基础char[]
或char*
(或任何形式)保留在中。str
getString()
main()
但是,显然不是这种情况,因为程序会输出垃圾。因此,问题是,什么时候被str
销毁,为什么?
getString()
将返回str
(getString()
按值返回)的副本;
这是正确的。
因此,的副本
str
将保持“活动”状态main()
直到main()
返回。
不,返回的副本是一个临时副本std::string
,它将在创建该副本的语句末尾(即before)销毁std::cout << cStr << std::endl;
。然后cStr
变得悬而未决,对其取消引用会导致UB,一切皆有可能。
您可以将返回的临时变量复制到命名变量,或将其绑定到const
lvalue-reference或rvalue-reference(临时变量的寿命将延长,直到引用超出范围)。如:
std::string s1 = getString(); // s1 will be copy initialized from the temporary
const char* cStr1 = s1.c_str();
std::cout << cStr1 << std::endl; // safe
const std::string& s2 = getString(); // lifetime of temporary will be extended when bound to a const lvalue-reference
const char* cStr2 = s2.c_str();
std::cout << cStr2 << std::endl; // safe
std::string&& s3 = getString(); // similar with above
const char* cStr3 = s3.c_str();
std::cout << cStr3 << std::endl; // safe
这是[The.C ++。Programming.Language.Special.Edition] 10.4.10临时对象[class.temp]的解释:
除非绑定到引用或用于初始化命名对象,否则临时对象将在创建该对象的完整表达式的末尾销毁。完整表达式是不是其他表达式的子表达式的表达式。
标准字符串类具有成员函数c_str(),该函数返回C样式,零终止字符数组(第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´) { // cs used here } }
您的第一个反应可能是“但不要那样做”,我同意。但是,此类代码确实可以编写,因此值得了解如何解释。
将创建一个类字符串的临时对象来容纳s1 + s2。接下来,从该对象提取指向C样式字符串的指针。然后-在表达式的末尾-删除临时对象。现在,C风格的字符串分配在哪里?可能是临时对象s1 + s2的一部分,并且在该临时对象被销毁后不能保证存在该存储。因此,cs指向释放的存储。输出操作cout << cs可能会按预期工作,但这真是运气。编译器可以检测到此问题并针对此问题发出警告。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句