我首先要说的是我读过这个主题:C++ Return reference / stack memory。但在那里,问题是std::vector<int>
作为对象类型。但我虽然std::string
的行为不同。这个类是不是特别为使用字符串而不必担心内存泄漏和错误的内存使用?
所以,我已经知道这是错的:
std::vector<t> &function()
{
vector<t> v;
return v;
}
但这也错了吗?
std::string &function()
{
string s = "Faz";
s += "Far";
s += "Boo";
return s;
}
由于
额外问题(编辑):所以,当我说返回(按值)时,我是正确的std::string
不会复制字符序列,只有指向char *
数组的指针和长度为t_size
的指针?
如果此语句正确,这是否是创建字符串深层副本的有效方法(以避免替换会改变字符串)?
string orig = "Baz";
string copy = string(orig);
答案 0 :(得分:23)
类型是什么并不重要;对于任何对象类型T
来说,此模式总是完全错误100%:
T& f() {
T x;
return x;
} // x is destroyed here and the returned reference is thus unusable
如果从函数返回引用,则必须确保函数返回后它所引用的对象仍然存在。由于具有自动存储持续时间的对象在声明它们的块的末尾被销毁,因此在函数返回后它们将保证不存在。
答案 1 :(得分:21)
你真的很接近让这些功能发挥作用:
std::string function()
{
string s = "Faz";
s += "Far";
s += "Boo";
return s;
}
只需让它们返回副本而不是引用即可。这就是你想要的,基于堆栈的字符串的副本。
它也会变得更好,因为返回值优化(RVO)只会创建一次字符串并返回它,就像你在堆上创建它并返回它的引用一样,都在幕后!
答案 2 :(得分:5)
不要返回引用,按值返回:
std::string function() // no ref
{
string s = "Faz";
s += "Far";
s += "Boo";
return s;
}
如果您的编译器可以执行命名返回值优化,也就是NRVO(很可能),它会将其转换为大致相当于以下内容的内容,从而避免任何无关的副本:
// Turn the return value into an output parameter:
void function(std::string& s)
{
s = "Faz";
s += "Far";
s += "Boo";
}
// ... and at the callsite,
// instead of:
std::string x = function();
// It does this something equivalent to this:
std::string x; // allocates x in the caller's stack frame
function(x); // passes x by reference
关于额外的问题:
字符串的复制构造函数始终执行深层复制。因此,如果涉及副本,则不存在别名问题。但是,如上所述,当使用NRVO按值返回时,不会复制。
您可以使用多种不同的语法制作副本:
string orig = "Baz";
string copy1 = string(orig);
string copy2(orig);
string copy3 = orig;
第二个和第三个没有语义差异:它们都只是初始化。第一个通过显式调用复制构造函数创建临时,然后使用副本初始化变量。但编译器可以在这里复制省略(并且它很可能会复制)并且只会复制一份。
答案 3 :(得分:1)
这个问题(无论类型如何)是你在返回命中时返回对超出范围的内存的引用。
std::string &function()
{
string s = "Faz";
s += "Far";
s += "Boo";
// s is about to go out scope here and therefore the caller cannot access it
return s;
}
您可能希望将返回类型更改为不是引用而是按值更改,因此会返回s的副本。
std::string function()
{
string s = "Faz";
s += "Far";
s += "Boo";
// copy of s is returned to caller, which is good
return s;
}
答案 4 :(得分:0)
您可以获取返回字符串的地址,并将其与原始字符串的地址进行比较,如下所示:
orig
我得到了以下内容:
orig addr:0x7ffccb085230
copy1 addr:0x7ffccb0851a0
copy2 addr:0x7ffccb0851c0
copy3 addr:0x7ffccb0851e0
ret addr:0x7ffccb085230
您看到ret
和orig
指向内存中的相同字符串实例,因此引用返回copy1
。 copy2
,copy3
,orig
是reg LED=1;
的副本,因为它们指向内存中的不同对象。