Когда вы должны вернуть ссылку на объект из метода класса

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

Ответ 1

Я предполагаю, что методом класса вы подразумеваете функцию-член. И что при помощи "return by reference" вы имеете в виду "обратную ссылку на данные участника". Это в основном в противоположность возврату ссылки на местную, что явно неверно.

Когда вы должны возвращать ссылку на данные участника и когда сами данные?

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

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

  • Пользовательский код становится зависимым от точного типа возврата. Например, для реализации используется vector<T> (и то, что возвращает ваш получатель). Появится код пользователя, например "vector<T> foo = obj.getItems()". Затем вы меняете свою реализацию (и getter) на использование разрывов кода deque<T> - user. Если вы возвращались по значению, вы могли бы просто заставить геттер создать локальный вектор, скопировать данные из деления участника и вернуть результат. Довольно разумно для небольших коллекций. [*]

Итак, когда вы должны вернуть ссылку?

  • Вы можете рассмотреть это, когда возвращаемый объект огромен (Image) или не скопирован (boost::signal). Но, как всегда, вместо этого вы можете выбрать больше шаблонов ООП, имеющих свой класс сделать, а не навешивать на него . В случае Image вы можете предоставить функцию члена drawCircle, а не возвращать Image&, а ваши пользователи нарисуют на ней круг.
  • Когда ваши данные логически принадлежат вашему пользователю, и вы просто держите его за него. Рассмотрим std коллекции: vector<T>::operator[] возвращает ссылку на T, потому что я хочу получить: мой точный объект, а не его копию.

[*] Существует лучший способ обеспечить будущий код. Вместо того, чтобы возвращать вектор (по рефлексию по значению), верните пару итераторов к вашему вектору - началу и концу. Это позволяет вашим пользователям делать все, что они обычно делают с помощью deque или vector, но не зависит от фактической реализации. Boost обеспечивает boost::iterator_pair для этой цели. В качестве перка он также перегружен оператором [], поэтому вы можете даже сделать "int i = obj.getItems()[5]", а не "int i = obj.getItems().begin()[5]".

Это решение является обобщающим для любой ситуации, которая позволяет обрабатывать типы в целом. Например, если вы сохраняете член Dog, но ваши пользователи должны знать его только Animal (потому что они только вызывают eat() и sleep()), верните ссылку/указатель Animal на выделенную freestore копию Ваша собака. Затем, когда вы решаете, что собаки - слабаки, и вам действительно нужен волк для реализации, код пользователя не сломается.

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

Ответ 2

Перегрузка операторов присваивания (например, =, + =, - = и т.д.) является хорошим примером, когда возвращение по ссылке имеет большой смысл. Такие методы, очевидно, возвращают большие объекты, и вы не хотите возвращать указатели, поэтому возвращение ссылки - лучший способ. Работает как указатель и выглядит как возвращающийся по значению.

Ответ 3

Ссылка - это маскировка указателя (так что это 4 байта на 32-битных машинах и 8 байтов на 64-битных машинах). Итак, эмпирическое правило: если копирование объекта дороже, чем возврат указателя, используйте указатель (или ссылку, поскольку это то же самое).

Какие типы дороже копировать, зависит от архитектуры, компилятора, самого типа и т.д. В некоторых случаях копирование объекта, в котором 16 байтов может быть быстрее, чем возврат указателя на него (например, если объект сопоставляется с SSE регистрации или аналогичной ситуации).

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

Бывают ситуации, когда вы не хотите возвращать указатель/ссылку на объект, даже если копирование объекта дорого. В основном, когда вы не хотите привязывать код вызова к времени жизни исходного объекта.

Ответ 4

Я бы рекомендовал держаться подальше от ссылок по той же причине, что и Iraimbilanja, но, на мой взгляд, вы можете получить очень хорошие результаты, используя общие указатели (например, boost, tr1) для данных членов и использовать их взамен, Таким образом, вам не нужно копировать объект, но он все равно может управлять проблемами времени жизни.

class Foo
{
private:
    shared_ptr<Bar> _bar;
public:
    shared_ptr<Bar> getBar() {return _bar;}
};

Обычно стоимость копирования Bar больше стоимости создания новых shared_ptrs, если это не так, ее все равно можно использовать для управления жизненным циклом.

Ответ 5

В книге Скотта Мейерса "Эффективный С++" есть несколько тем, связанных с этой темой. Я определенно проверил бы элемент под названием "Не пытайтесь вернуть ссылку, когда вы должны возвращать объект". Это элемент № 23 в 1-м или 2-м выпусках или № 21 в 3-м издании.

Ответ 6

если NULL - возможное возвращаемое значение, метод должен возвращать указатель, потому что вы не можете вернуть ссылку на NULL.

Ответ 7

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

Возвращайте объекты класса (даже std::string) по ссылке.