C++:从堆栈内存返回 std::string 引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6388698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:01:41  来源:igfitidea点击:

C++: Return std::string reference from stack memory

c++memory-managementreference

提问by Martijn Courteaux

I will start by saying I've read this topic: C++ Return reference / stack memory. But there, the question was with an std::vector<int>as object-type. But I though the behavior of std::stringwas different. Wasn't this class especially made for using strings without having to worry about memory-leaks and wrong usage of memory?

我将首先说我已经阅读了这个主题:C++ 返回引用/堆栈内存。但在那里,问题在于std::vector<int>as 对象类型。但我虽然行为std::string是不同的。这个类不是专门为使用字符串而设计的,而不必担心内存泄漏和错误使用内存吗?

So, I already know this is wrong:

所以,我已经知道这是错误的:

std::vector<t> &function()
{
    vector<t> v;
    return v;
}

But is this wrong as well?

但这也是错误的吗?

std::string &function()
{
    string s = "Faz";
    s += "Far";
    s += "Boo";
    return s;
}

Thanks

谢谢



Extra question (EDIT):So, am I correct when I say returning (by value)an std::stringdoesn't copy the char sequence, only a pointer to the char *array and an t_sizefor the length?

额外的问题(编辑):所以,我是正确的,当我说回(按价值计算)std::string不复制的字符序列,只有一个指向char *数组和t_size用于长度是多少?

If this statement is correct, is this the valid way to create a deep copy of a string (to avoid replacing will alter the string)?

如果此语句是正确的,这是创建字符串深层副本的有效方法(避免替换会改变字符串)吗?

string orig = "Baz";
string copy = string(orig);

回答by James McNellis

It doesn't matter what the type is; this pattern is always completely, 100% wrong for any object type T:

类型是什么并不重要;对于任何对象类型,此模式始终是完全错误的,100% 错误T

T& f() {
    T x;
    return x;
}   // x is destroyed here and the returned reference is thus unusable

If you return a reference from a function, you must ensure that the object to which it refers will still exist after the function returns. Since objects with automatic storage duration are destroyed at the end of the block in which they are declared, they are guaranteed notto exist after the function returns.

如果从函数返回引用,则必须确保在函数返回后它引用的对象仍然存在。由于具有自动存储期的对象在声明它们的块的末尾被销毁,因此它们保证在函数返回后存在。

回答by Blindy

You are really close to making those functions work:

你真的很接近使这些功能工作:

std::string function()
{
    string s = "Faz";
    s += "Far";
    s += "Boo";
    return s;
}

Simply make them return a copy instead of a reference and you're set. This is what you want, a copy of the stack-based string.

只需让他们返回一个副本而不是一个引用,你就设置好了。这就是您想要的,基于堆栈的字符串的副本。

It gets better too, because the return value optimization (RVO) will only create the string once and return it, just like if you had created it on the heap and returned a reference to it, all behind the scenes!

它也变得更好了,因为返回值优化 (RVO) 只会创建一次字符串并返回它,就像您在堆上创建它并返回对它的引用一样,所有这些都在幕后!

回答by R. Martinho Fernandes

Don't return references, return by value:

不返回引用,按值返回:

std::string function() // no ref
{
    string s = "Faz";
    s += "Far";
    s += "Boo";
    return s;
}

If your compiler can do named return value optimization, aka NRVO, (which is likely), it will transform this into something roughly equivalent to the following, which avoids any extraneous copies:

如果您的编译器可以执行命名返回值优化,又名 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


Regarding the extra question:

关于额外的问题:

The copy constructor of string always does a deep copy. So, if there are copies involved, there are no aliasing issues. But when returning by value with NRVO, as you can see above, no copies are made.

string 的复制构造函数总是进行深复制。因此,如果涉及副本,则不存在混叠问题。但是当使用 NRVO 按值返回时,如您所见,不会生成任何副本。

You can make copies using several different syntaxes:

您可以使用几种不同的语法制作副本:

string orig = "Baz";
string copy1 = string(orig);
string copy2(orig);
string copy3 = orig;

The second and third have no semantic difference: they're both just initialization. The first one creates a temporary by calling the copy constructor explicitly, and then initializes the variable with a copy. But a compiler can do copy elision here (and it's very likely that it will) and will make only one copy.

第二个和第三个没有语义差异:它们都只是初始化。第一个通过显式调用复制构造函数创建一个临时变量,然后用一个副本初始化变量。但是编译器可以在这里进行复制省略(并且很可能会)并且只会制作一个副本。

回答by Scott Saad

The problem with this (regardless of the type) is that you're returning a reference to memory that goes out of scope oncee the return is hit.

与此有关的问题(无论类型如何)是,一旦命中返回,您将返回对超出范围的内存的引用。

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;
}

You would want to change the return type to not be reference but by value, therefore a copy of s gets returned.

您可能希望将返回类型更改为不是引用而是按值,因此将返回 s 的副本。

std::string function()
{
    string s = "Faz";
    s += "Far";
    s += "Boo";

    // copy of s is returned to caller, which is good
    return s;
}

回答by Pavel

You can take the address of the returned string and compare it with the address of the original string, as shown below:

可以将返回字符串的地址与原字符串的地址进行比较,如下图:

#include <iostream>    
using namespace std;

string f() {
    string orig = "Baz";
    string copy1 = string(orig);
    string copy2(orig);
    string copy3 = orig;

    cout << "orig addr: " << &orig << endl;
    cout << "copy1 addr: " << &copy1 << endl;
    cout << "copy2 addr: " << &copy2 << endl;
    cout << "copy3 addr: " << &copy3 << endl;
    return orig;
}

int main() {
    string ret = f();
    cout << "ret addr: " << &ret << endl;
}

I got the following:

我得到以下信息:

orig addr: 0x7ffccb085230
copy1 addr: 0x7ffccb0851a0
copy2 addr: 0x7ffccb0851c0
copy3 addr: 0x7ffccb0851e0
ret addr: 0x7ffccb085230

原始地址:0x7ffccb085230
copy1 地址:0x7ffccb0851a0
copy2 地址:0x7ffccb0851c0
copy3 地址:0x7ffccb0851e0
ret 地址:0x7ffccb085230

You see origand retpoint to the same string instance in memory, so origis returned by reference. copy1, copy2, copy3are copies of origbecause they point to different objects in memory.

您看到origret指向内存中的同一个字符串实例,因此orig通过引用返回。copy1, copy2,copy3是 的副本,orig因为它们指向内存中的不同对象。