Почему люди пишут получатели частного поля, возвращающие неконстантную ссылку?

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

class foo {
private:
    int integer_;
    string someString_;
    // other variables
public:
    int& integer() { return integer_; }
    string& someString() { return someString_; }
    // other "functions"
}

int main() {
    foo f;
    f.integer() = 10;
    f.someString() = "something";
    return 0;
}

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

Почему это обычно используется?

Ответ 1

Там повторяющаяся мантра, что функции getter/setter должны использоваться для инкапсуляции ваших данных. Поэтому многие (неопытные или перегруженные кофе) программисты получают идею, что они должны использовать что-то вроде:

int& integer() { return integer_; }

но это не сильно отличается от простого написания:

class foo {
public: // <<<
    int integer_;
    string someString_;
    // ...
};

Ну, он добавляет вызов функции, но вы не можете контролировать то, что делает клиент со ссылкой.


Если вы действительно хотите предоставить функцию getter, напишите:

const int& integer() const { return integer_; }

Соответствующая функция сеттера выглядит следующим образом:

void integer(const int& value) {
    integer_ = value;
}

Ответ 2

Я должен частично не согласиться с ответами @πάνταῥεῖ и @Rakete1111, учитывая, как определение класса является тем, что может развиваться со временем.

Хотя верно, что часто эти методы getter написаны кем-то, кто только что услышал мантру "без разоблачения", они также могут иметь законное использование:

  • Метод getter позже может быть изменен, чтобы включить какую-либо проверку достоверности или код выделения ресурсов, прежде чем возвращать ссылку, - прямой доступ к элементу данных не позволяет. Хотя это означает изменение кода класса, это не требует изменения кода пользователя класса. Кроме того, если реализация геттера не отображается в заголовке класса, возможно, даже не потребуется перекомпилировать код пользователя класса. Примечание. Выполнение этого, вероятно, является признаком другого плохого выбора дизайна.
  • Метод getter может быть переопределен подклассом (в этом случае он часто делается виртуальным методом) аналогично выше.
  • Метод getter позже может заменить возвращаемый тип proxy для исходного типа элемента данных, а не ссылкой на фактический член, который больше не может существовать. Подумайте, как работает vector<bool>; когда вы вызываете его operator[], вы не получаете boolean&, вы получаете какой-то прокси-сервер, который при назначении или назначении выполняет соответствующее извлечение или настройку бит.
  • IIANM, геттер не может использоваться для не-const-экземпляров. Таким образом, на самом деле это ограничивает доступ к тому, чтобы разоблачить участника. Независимо от того, действительно ли автор класса имел в виду, что это другой вопрос...

Подводя итог: Получатель "dummy" не-const-reference может быть заглушкой для другого, значимого кода.

Это, как говорится, часто является хорошей идеей просто заставить геттер вернуть ссылку на константу или значение. Или просто выставлять поле в тех случаях, когда оно подходит (и некоторые из них тоже).

Ответ 3

Я бы решительно отказался возвращать ссылку на частную переменную. Не потому, что он разрушает инкапсуляцию, а потому, что это не нужно: почему бы не сделать переменную public в первую очередь?

Нарушение инкапсуляции плохо, да, но это не означает, что каждая переменная должна быть private. Некоторые переменные предназначены для чтения и изменения от пользователя, поэтому имеет смысл сделать их public. Например, возьмите std::pair, он имеет две переменные public member, first и second. Это неплохая практика.

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

Ответ 4

Эта конструкция может использоваться для целей отладки.

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

Однако раздельный getter и setter будут работать с той же целью еще лучше, поэтому это просто преимущество перед обычными переменными public.

Ответ 5

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

Своего одержимого техником. Ни один из других кодеров в проекте не мог понять это вообще. Весь стек был вырван.