Должны ли методы доступа возвращать значения или постоянные ссылки? - PullRequest
16 голосов
/ 06 января 2011

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

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

или

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

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

Ответы [ 8 ]

16 голосов
/ 06 января 2011

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

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

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

7 голосов
/ 06 января 2011

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

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

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

4 голосов
/ 06 января 2011

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

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

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

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

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

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

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


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

2 голосов
/ 06 января 2011

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

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

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

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

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

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

1 голос
/ 06 января 2011

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

0 голосов
/ 06 января 2011

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

int getData() const;
short getMoreData() const;
const std::string& getName() const;
const ComplexObject& getComplexData() const;
0 голосов
/ 06 января 2011

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

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

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

0 голосов
/ 06 января 2011

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

Это лучше, если вы просто хотите сделать запрос, а не изменять 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;
}
...