Ответ 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 копию Ваша собака. Затем, когда вы решаете, что собаки - слабаки, и вам действительно нужен волк для реализации, код пользователя не сломается.
Такое скрытие информации делает больше, чем обеспечение будущей совместимости. Это также помогает поддерживать чистоту вашего дизайна.