Безопасно ли возвращать std :: string по значению?

В следующем коде строка инкапсулирована в классе Foo.

Вызов Foo :: getText () возвращает строку по значению. Это создает второй экземпляр строкового объекта, но оба строковых объекта теперь указывают на один и тот же символьный буфер в куче.

Когда экземпляр Foo удаляется, инкапсулированная строка автоматически уничтожается, и поэтому буфер символов в куче удаляется.

Даже если getText() возвращает строку по значению, результирующий строковый объект по-прежнему полагается на время жизни исходного строкового объекта, чтобы сохранить буфер символов, на который они взаимно указывают.

Разве это не означает, что печать retval к терминалу есть неверный доступ к памяти, которая уже была свободна в куче?

class Foo
{
Foo(const char* text) :
str(text)
{
}

std::string getText()
{
return str;
}

std::string str;
};

int main()
{
Foo pFoo = new Foo("text");
std::string retval = foo.getText();
delete pFoo;
cout << retval;  // invalid memory access to char buffer?
}

Я думаю, что многие люди предполагают, что, поскольку строка была возвращена по значению, им не нужно беспокоиться о времени жизни исходной строки в Foo. Эта проблема не связана строго со строками, но действительно относится к любому классу с инкапсулированными указателями, которые освобождаются при уничтожении. Но какая здесь самая лучшая практика, когда дело доходит до струн?

  1. Никогда не возвращать строку по значению?
  2. Возвращать строки только по значению, если время жизни исходной строки гарантировано?
  3. Всегда делать копию строки? return std::string(retval.c_str());
  4. Обеспечить заключение договора с абонентом getText()?

РЕДАКТИРОВАТЬ:

Я думаю, что я был введен в заблуждение RVO. Все три строки в этом примере возвращают c_str по одному и тому же адресу. Виноват ли РВО?

class Obj
{
public:
Obj() : s("text")
{
std::printf("%p\n", s.c_str());
}

std::string getText() { return s; }

std::string s;
};

int main()
{
Obj* pObj = new Obj();
std::string s1(pObj->getText());
std::string s2 = pObj->getText();
delete pObj;
std::printf("%p\n", s1.c_str());
std::printf("%p\n", s2.c_str());
}

Результат:

0x600022888
0x600022888
0x600022888

-2

Решение

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

Нет, они не

std::stringвладеют своим содержанием. Когда вы копируете std::stringкопируешь свой буфер.

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

И эти люди правы. Здесь нет «обмена».

Ваш возврат по стоимости в порядке, и вам не нужно больше думать об этом.

10

Другие решения

Я хотел бы добавить несколько моментов к ответу @ LightnessRacesInOrbit:

Никогда не возвращать строку по значению?

Никогда не возвращайся местный stringс помощью ссылки или указателя. На самом деле, никогда не вернусь что-нибудь локально по ссылке или по указателю. По стоимости просто отлично.

Возвращать строки только по значению, если время жизни исходной строки
гарантировано?

Опять ты думаешь задом наперед. Только возврат по ссылке или указателю если время жизни оригинала гарантировано.

Всегда делать копию строки? вернуть std :: string (retval.c_str ());

C ++ делает это автоматически для вас, если он не может переместить строку (RVO / NRVO). Не нужно копировать вручную.

Принудительно заключить договор с вызывающей стороной getText ()?

Не нужно, так как вы все равно получаете копию

Я думаю, что многие люди предполагают, что, поскольку строка была возвращена по значению, им не нужно беспокоиться о времени жизни исходной строки в Foo. Эта проблема не связана строго со строками, но действительно относится к любому классу с инкапсулированными указателями, которые освобождаются при уничтожении.

И их предположение верно. Любой класс с (рабочим) конструктором копирования можно копировать так часто, как это необходимо, и каждый скопированный экземпляр полностью независим от других. Конструктор копирования заботится о копировании пространства кучи.

5

По вопросам рекламы [email protected]