например.
Что лучше всего из них:
std::string f() {}
или
const std::string& f() {}
например.
Что лучше всего из них:
std::string f() {}
или
const std::string& f() {}
Функция никогда не должна возвращать ссылку на локальный объект/переменную, поскольку такие объекты выходят из области видимости и уничтожаются при возврате из функции.
Иными словами, функция может возвращать постоянную или неконстантную ссылку на объект, область действия которого не ограничена контекстом функции. Типичным примером является пользовательский operator<<
:
std::ostream & operator<<(std::ostream &out, const object &obj)
{
out << obj.data();
return out;
}
К сожалению, возврат по значению имеет свой недостаток производительности. Как упоминал Крис, возвращение объекта по значению включает в себя копию временного объекта и его последующее уничтожение. Копирование выполняется с помощью либо конструктора копирования, либо operator=
. Чтобы избежать этой неэффективности, умные компиляторы могут применять оптимизации RVO или NRVO, но в некоторых случаях они не могут - многократные возвраты.
В готовящемся стандарте С++ 0x, частично доступном в gnu gcc-4.3, вводится ссылка на rvalue [& & amp;], которая может использоваться для различения lvalue от ссылки на rvalue. Благодаря этому можно реализовать конструктор перемещения, полезный для возврата объекта, частично избегая затрат на конструктор копирования и деструктор временного объекта.
Конструктор перемещения - это то, что Андрей задумал несколько лет назад в статье http://www.ddj.com/database/184403855, предложенной Крисом.
Конструктор перемещения имеет следующую подпись:
// move constructor
object(object && obj)
{}
и предполагается, что он переходит во владение внутренними объектами переданного объекта, оставляя последний в состоянии по умолчанию. Благодаря этому можно избежать копий внутренних органов и упростить уничтожение временных. Типичная фабрика функций будет иметь следующую форму:
object factory()
{
object obj;
return std::move(obj);
}
std::move()
возвращает ссылку на значение из объекта. И последнее, но не менее важное, конструкторы перемещения позволяют возвращать ссылку на неопределяемые объекты.
Я хочу добавить к Николау отличный ответ. Да, вы никогда не должны возвращать оборванную ссылку (например, ссылку на локальную переменную), однако есть три полезных способа повысить производительность в этих случаях:
Оптимизация возвращаемого значения (RVO): вы возвращаетесь по значению, но исключаете копирование, имея только один оператор return
, который создает возвращаемое значение на месте. Вот пример использования RVO: Как я могу токенизировать строку С++?
Именованная оптимизация возвращаемых значений (NRVO): вы возвращаетесь по значению и сначала объявляете переменную возвращаемого значения в верхней части функции. Все операторы return
возвращают эту переменную. С компиляторами, которые поддерживают NRVO, эта переменная выделяется в слоте возвращаемого значения и не копируется при возврате. например.
string
foobar()
{
string result;
// fill in "result"
return result;
}
Используйте shared_ptr
или тому подобное как возвращаемый тип; это требует создания вашего объекта в куче, а не в стеке. Это предотвращает проблемы с обвязывающей ссылкой, но при этом не требуется копировать весь объект, просто умный указатель.
Кстати, я не могу взять кредит на информацию о RVO и NRVO; они выходят прямо из Скотта Мейера Более эффективный С++. Поскольку в настоящее время у меня нет книги со мной, любые ошибки в моем описании - это мои действия, а не Скотта.: -)
Если вы вернете ссылку на переменную, которая является локальной для этой функции, тогда вы столкнетесь с проблемами (в зависимости от компилятора и его параметров).
Если вы вернете ссылку на экземпляр, который все еще находится в области видимости, когда функция возвращается, то она будет быстрее, так как копия строки не создается.
Таким образом, последний более эффективен (технически), но может работать не так, как ожидалось, в зависимости от того, на что вы возвращаете ссылку.