Должны ли аксессоры возвращать значения или постоянные ссылки?

Предположим, что у меня есть класс Foo с элементом std::string str. Что следует вернуть get_str?

std::string Foo::get_str() const
{
    return str;
}

или

const std::string& Foo::get_str() const
{
    return str;
}

Что более идиоматично в С++?

Ответы

Ответ 1

Короткий ответ: это зависит: -)

С точки зрения производительности возврат ссылки (обычно) лучше: вы сохраняете создание нового объекта std::string. (В этом случае создание достаточно дорого и размер объекта достаточно высок, чтобы justify сделать этот выбор, по крайней мере, заслуживающим внимания, но это не всегда так. С меньшим или встроенным значением, в типе разница в производительности может быть незначительной, или возврат по значению может даже быть дешевле).

С точки зрения безопасности возвращение копии исходного значения может быть лучше, поскольку созвездие может быть отброшено вредоносными клиентами. Это особенно важно учитывать, если этот метод является частью общедоступного API, т.е. Вы (r team) не имеете полного контроля над тем, как используется возвращаемое значение (неверно).

Ответ 2

Одной из целей использования метода доступа является попытка, по крайней мере, в некоторой степени отвлечь реализацию вашего класса от его интерфейса.

Возврат по значению лучше, потому что не существует проблем с продолжительностью жизни с указанным объектом. Если вы решили не иметь члена std::string, но, скажем, std::stringstream или создать std::string на лету, вам не нужно менять интерфейс.

Возвращение с помощью ссылки const не является противоположностью взятию параметра по ссылке const, взяв значение const, ссылка не привязывает ваше внутреннее представление данных к внешнему интерфейсу.

Ответ 3

В общем случае (если нет доказанной проблемы с производительностью), я бы вернулся по значению.

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

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

Другая проблема заключается в многопоточном коде, если один поток читает из ссылки const, когда вы обновляете переменную, в которой есть много проблем.

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

string val = obj->get_str();
// use val now

Если это правда (в отличие от cout << obj->get_str(), где нет переменной), вам всегда нужно построить новую строку для val, даже если вы вернетесь по ссылке и поскольку компиляторы могут выполнить RVO, версия по умолчанию не будет недооценивать вариант by-const-ref.


В заключение:, если вы знаете, что проблема с производительностью и уверенна, что возвращаемое значение не будет храниться дольше, чем ваш объект будет существовать и, который вы не ожидаете использовать из разных потоков, а затем OK для возврата по ссылке const.

Ответ 4

Возврат по значению означает, что вам не нужно иметь внутренний std::string, который хранится где-то в классе, для которого вы возвращаетесь.

В чистом виртуальном методе предпочтительнее не предполагать, что будет std::string и, следовательно, вернуть значение std::string по значению.

В конкретном классе, где явно присутствует член std::string, и вы только собираетесь вернуть ссылку на него, вы можете для эффективности вернуть его ссылкой const. Даже если вам нужно изменить его позже, вам не нужно менять функциональность, использующую класс.

В многопоточной модели, где внутренняя строка может меняться между вызовами, конечно, вам, вероятно, нужно вернуться по значению (если предположить, что пользователи класса получат представление "моментального снимка" строкового значения во время завершение вызова).

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

Кстати, кто-то порекомендовал бы возвращать значение std::string по const. Я не думаю, что это лучший способ сделать это, поскольку это мешает пользователю "поменять" его на локальную переменную.

Ответ 5

AFAIK, правило такое же, как правило, которое используется при принятии решения о необходимости использования параметра функции по значению или константной ссылке. Если sizeof возвращаемое значение является достаточно маленьким, я предпочитаю использовать возвращаемый экземпляр else, возвращающий ссылку const.

Ответ 6

Я полагаю, что вторая реализация (константная ссылка) верна как:

  • возвращаемый объект является неизменным и, следовательно, поддерживает правила инкапсуляции.
  • он немного более эффективен, так как нет копирования str.

Однако первый подход будет работать почти так же.

Ответ 7

Как правило, вы должны возвращать POD по значению (например, int, short, char, long и т.д.) и константную ссылку для более сложных типов:

int getData() const;
short getMoreData() const;
const std::string& getName() const;
const ComplexObject& getComplexData() const;

Ответ 8

Это зависит от того, что вы хотите сделать с возвращаемым значением.

Это лучше, если вы просто хотите сделать запрос и не изменить str.

const std::string& Foo::get_str() const
{
    return str;
}

В противном случае перейдите к следующему:

std::string& Foo::get_str()
{
    return str;
}

И если вы хотите копировать/клонировать str, используйте это:

std::string Foo::get_str() const
{
    return str;
}