Возвращает ссылки на неверную практику переменных-членов?

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

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_;
};

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

auto & aref = a[1];

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