C ++: вернуть ссылку std :: string из стековой памяти - PullRequest
10 голосов
/ 17 июня 2011

Начну с того, что прочитал эту тему: C ++ Возвращаемая ссылка / стек памяти .Но там вопрос был с 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);

Ответы [ 5 ]

24 голосов
/ 17 июня 2011

Неважно, что это за тип;этот шаблон всегда полностью, на 100% неверен для любого типа объекта T:

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

Если вы возвращаете ссылку из функции, вы должны убедиться, что объект, на который он ссылается, будет существовать и послефункция возвращает.Поскольку объекты с автоматическим хранением уничтожаются в конце блока, в котором они объявлены, они гарантированно существуют , а не после возврата из функции.

23 голосов
/ 17 июня 2011

Вы действительно близки к тому, чтобы заставить эти функции работать:

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

Просто заставьте их вернуть копию вместо ссылки, и все готово.Это то, что вы хотите, копия строки на основе стека.

Это также улучшается, потому что оптимизация возвращаемого значения (RVO) создаст строку только один раз и вернет ее, как если бы вы создали ее в куче и вернули ссылку на нее, все за кулисами!

5 голосов
/ 17 июня 2011

Не возвращать ссылки, возврат по значению:

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;

Второе и третье не имеют семантической разницы: они оба просто инициализация. Первый создает временный объект, явно вызывая конструктор копирования, а затем инициализирует переменную копией. Но компилятор может сделать здесь elision copy (и очень вероятно, что так и будет) и сделает только одну копию.

1 голос
/ 17 июня 2011

Проблема с этим (независимо от типа) в том, что вы возвращаете ссылку на память, которая выходит из области видимости после того, как возвращение достигнуто.

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;
}
0 голосов
/ 20 февраля 2016

Вы можете взять адрес возвращаемой строки и сравнить его с адресом исходной строки, как показано ниже:

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

Я получил следующее:

адрес источника: 0x7ffccb085230
copy1 адрес: 0x7ffccb0851a0
copy2 адрес: 0x7ffccb0851c0
copy3 адрес: 0x7ffccb0851e0
ret addr: 0x7ffccb085230

Вы видите orig и ret, указывающие на один и тот же экземпляр строки в памяти, поэтому orig возвращается по ссылке. copy1, copy2, copy3 являются копиями orig, поскольку они указывают на разные объекты в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...