Какова наилучшая практика возврата ссылок из методов класса. В этом случае базовые типы, которые вы хотите вернуть без ссылки, тогда как объекты класса, которые вы хотите вернуть по ссылке. Любые статьи, рекомендации по лучшей практике, которые вы рекомендуете.
Когда вы должны вернуть ссылку на объект из метода класса
Ответ 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) по ссылке.