Возвращение ссылок на переменные-члены плохая практика? - PullRequest
45 голосов
/ 04 ноября 2011

Ниже сказано, что лучше, чем иметь первых / вторых в качестве публичных участников.Я считаю, что это почти так же плохо.Если вы предоставляете способ доступа к закрытой переменной за пределами класса, то какой в ​​этом смысл?Разве функции не должны быть

T First(); void(or T) First(const T&)

Пример:

// Example 17-3(b): Proper encapsulation, initially with inline accessors. Later
// in life, these might grow into nontrivial functions if needed; if not, then not.
//
template<class T, class U>
class Couple {
  Couple()           : deleted_(false) { }
  T& First()         { return first_; }
  U& Second()        { return second_; }
  void MarkDeleted() { deleted_ = true; }
  bool IsDeleted()   { return deleted_; }

private:
 T first_;
 U second_;
 bool deleted_;
};

Ответы [ 3 ]

51 голосов
/ 04 ноября 2011

Существует несколько причин, по которым возврат ссылок (или указателей) на внутренние компоненты класса является плохим.Начиная с (что я считаю) самого важного:

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

  2. Инвариант больше не является устойчивым (в случае неконстантной ссылки): любой может получить доступ и изменить указанный атрибутпо желанию, таким образом, вы не можете «отслеживать» его изменения.Это означает, что вы не можете поддерживать инвариант, частью которого является этот атрибут.По сути, ваш класс превращается в BLOB-объект.

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

Поэтому лучше не давать ссылки или указатели на атрибуты. Даже не константные!

Для небольших значений обычно достаточно передать их копией (как in, так и out), особенно сейчас с семантикой перемещения (в путив).

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

Наконец, обратите внимание, что для некоторых классов наличие открытых членов не так уж плохо.Какой смысл заключать в капсулу членов pair?Когда вы обнаружите, что пишете класс, который является не более чем набором атрибутов (без каких-либо инвариантов), тогда вместо того, чтобы брать на себя всю ОО и писать пару геттер / сеттер для каждого из них, рассмотрите возможность сделать их открытыми.

25 голосов
/ 04 ноября 2011

Если template типы T и U являются большими структурами, то возврат по значению обходится дорого .Однако вы правы, что возврат по ссылке эквивалентен предоставлению доступа к переменной private.Чтобы решить обе проблемы, сделайте их const ссылки :

const T& First()  const  { return first_; }
const U& Second() const  { return second_; }

PS Кроме того, не рекомендуется хранить переменные неинициализированными внутри конструктора, когда естьнет метода установки.Кажется, что в исходном коде First() и Second() являются обертками над first_ и second_, которые предназначались для чтения и записи.

7 голосов
/ 05 марта 2014

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

std::map<int,std::string> a;
a[1] = 1;

. Ничто не мешает вам сделать

auto & aref = a[1];

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

...