Когда следует возвращать ссылку на объект из метода класса - PullRequest
11 голосов
/ 30 января 2009

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

Ответы [ 7 ]

6 голосов
/ 30 января 2009

Я предполагаю, что под методом класса вы подразумеваете функцию-член. И что под «возвратом по ссылке» вы подразумеваете «вернуть ссылку на данные члена». Это в основном в отличие от возврата ссылки на local, что явно неверно.

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

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

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

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

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

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

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

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

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

4 голосов
/ 30 января 2009

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

2 голосов
/ 30 января 2009

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

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

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

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

1 голос
/ 30 января 2009

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

1 голос
/ 30 января 2009

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

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

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

0 голосов
/ 08 мая 2009

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

0 голосов
/ 30 января 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...