Являются ли функции get и set популярными у программистов на C ++?

Я из мира C # изначально, и я изучаю C ++. Мне интересно, как получить и установить функции в C ++. В C # их использование довольно популярно, и такие инструменты, как Visual Studio, способствуют использованию, делая их очень легкими и быстрыми в реализации. Однако это не похоже на мир C ++.

Вот код C # 2.0:

public class Foo { private string bar; public string Bar { get { return bar; } set { bar = value; } } } 

Или, в C # 3.0:

 public class Foo { get; set; } 

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

Теперь, потому что я так привык к этому, я чувствую, что должен переносить эту привычку на свой код на C ++, но действительно ли это необходимо? Я не вижу, чтобы это делалось так часто, как с C #.

Во всяком случае, вот C ++ из того, что я собираю:

 class Foo { public: std::string GetBar() const; // Thanks for the tip Earwicker. void SetBar(std::string bar); private: std::string bar; } const std::string Foo::GetBar() { return bar; } void Foo::SetBar(std::string bar) { // Also, I always wonder if using 'this->' is good practice. this->bar = bar; } 

Теперь мне кажется, что много работы на ногах; учитывая использование инструментов Visual Studio, реализация C # потребует буквально секунд для реализации, а C ++ потребовал бы гораздо больше времени для ввода – я считаю, что это не стоит усилий, особенно когда альтернатива составляет 5 строк:

 class Foo { public: std::string Bar; } 

Из того, что я собираю, это преимущества:

  • Вы можете изменить детали реализации для функций get и set, поэтому вместо возвращения частного поля вы можете вернуть что-то более интересное.
  • Вы можете удалить get / set позже и сделать его только для чтения / записи (но для открытого интерфейса, кажется, это не хорошо).

И недостатки:

  • Принимает возраст, чтобы напечатать, действительно ли это стоит усилий? Вообще говоря. В некоторых случаях преимущества приносят пользу, но я имею в виду, говоря о «хорошей практике», не так ли?

Ответ:

Почему я выбрал ответ с меньшим количеством голосов ? На самом деле я был очень близок к выбору ответа veefu ; однако мое личное мнение (которое, по-видимому, противоречиво), заключается в том, что ответ на вопрос о поджаривании пудинга.

С другой стороны, ответ, который я выбрал, по-видимому, утверждает обе стороны; Я думаю, что геттеры и сеттеры являются злыми, если их использовать чрезмерно (я имею в виду, когда это не обязательно и будет нарушать бизнес-модель), но почему бы нам не воспользоваться функцией GetBalance() ?

Разумеется, это было бы гораздо более универсальным, чем PrintBalance() ; что, если я хочу показать это пользователю по-другому, чем того, что хотел меня class? Теперь, в некотором смысле, GetBalance() может быть недостаточно релевантным, чтобы утверждать, что «геттеры и сеттеры хороши», потому что у него нет (или, может быть, не должно )) сопутствующего setter, и, говоря об этом, функция SetBalance(float f) может быть плохим (на мой взгляд), потому что это будет означать, что разработчик функции должен манипулировать учетной записью со стороны classа, что не очень хорошо.

Я бы сказал, что предоставление аксессуаров более важно в C ++, чем в C #.

C ++ не имеет встроенной поддержки свойств. В C # вы можете изменить публичное поле на свойство в основном без изменения кода пользователя. В C ++ это сложнее .

Для меньшего набора текста вы можете реализовать тривиальные сеттеры / геттеры как встроенные методы:

 class Foo { public: const std::string& bar() const { return _bar; } void bar(const std::string& bar) { _bar = bar; } private: std::string _bar; }; 

И не забывайте, что геттеры и сеттеры несколько злобны.

Рискуя быть аргументированным, я вернусь к противоположной точке зрения, с которой я впервые столкнулся при чтении «Holub on Patterns». Это была точка зрения, которая была очень сложной, но имела смысл для меня:

Геттеры и сеттеры злы

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

Представьте, что поле ‘std :: string Foo :: bar’ должно измениться со строки std :: string на другой строковый class, который, скажем, лучше оптимизирован или поддерживает другой набор символов. Вам нужно будет изменить поле личных данных, получателя, сеттера и весь клиентский код этого classа, который называет эти геттеры и сеттеры.

Вместо того, чтобы создавать свои classы для «предоставления данных» и «получения данных», спроектируйте их для «выполнения операций» или «предоставления услуг». Спросите себя, почему вы пишете функцию «GetBar». Что вы делаете с этими данными? Возможно, вы показываете эти данные или обрабатываете их. Является ли этот процесс лучше открытым как метод Foo?

Это не означает, что геттеры и сеттеры не имеют своей цели. В C # я считаю, что основной причиной их использования является взаимодействие с IDE Visual Studio GUI-design, но если вы написали их на C ++, вероятно, лучше всего сделать шаг назад, посмотреть на ваш дизайн и посмотреть, пропал, отсутствует.

Я попробую сделать макет для иллюстрации.

 // A class that represents a user's bank account class Account { private: int balance_; // in cents, lets say public: const int& GetBalance() { return balance_; } void SetBalance(int b) { balance_ = b; } }; class Deposit { private: int ammount_; public: const int& GetAmount() { return ammount_; } void SetAmmount(int a) { _balance = a; } }; void DoStuffWithAccount () { Account a; // print account balance int balance = a.GetBalance(); std::cout << balance; // deposit some money into account Deposit d(10000); a.SetBalance( a.GetBalance() + d.GetValue()); } 

Это не занимает много времени, чтобы понять, что это очень плохо спроектировано.

  1. Целые числа - это ужасный валютный тип данных
  2. Депозит должен быть функцией Счета

Получатели и сеттеры затрудняют решение проблем, так как клиентский код DoStuffWithAccount теперь привязан к типу данных, который использовался для реализации баланса учетной записи.

Итак, давайте пропустим этот код и посмотрим, что мы можем улучшить

 // A class that represents a user's bank account class Account { private: float balance_; public: void Deposit(float b) { balance_ += b; } void Withdraw(float w) { balance_ -= w; } void DisplayDeposit(std::ostream &o) { o << balance_; } }; void DoStuffWithAccount () { Account a; // print account balance a.DisplayBalance(std::cout); // deposit some money into account float depositAmt = 1000.00; a.Deposit(depositAmt); a.DisplayBalance(std::cout); } 

«Поплавок» - это шаг в правильном направлении. Конечно, вы могли бы изменить внутренний тип на «float» и по-прежнему поддерживать идиому getter / setter:

 class Account { private: // int balance_; // old implementation float balance_; public: // support the old interface const int& GetBalance() { return (int) balance_; } void SetBalance(int b) { balance_ = b; } // provide a new interface for the float type const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int?? void SetBalance(float b) { balance_ = b; } }; 

но не требуется много времени, чтобы понять, что устройство getter / setter удваивает вашу рабочую нагрузку и усложняет ситуацию, поскольку вам необходимо поддерживать как код, использующий ints, так и новый код, который будет использовать float. Функция «Депозит» упрощает расширение диапазона типов для депонирования.

Учетный class, вероятно, не лучший пример, так как «получение» остатка на счете является естественной операцией для учетной записи. Главное, однако, в том, что вы должны быть осторожны с геттерами и сеттерами. Не входите в привычку писать геттеры и сеттеры для каждого члена данных. Очень легко разоблачить и заблокировать себя в реализации, если вы не будете осторожны.

В вашем примере:

 class Foo { public: const std::string GetBar(); // Should this be const, not sure? 

Вы, вероятно, имеете в виду следующее:

 std::string GetBar() const; 

Помещение const в конец означает: «Эта функция не изменяет экземпляр Foo, на который он вызван», поэтому он отмечает его как чистый геттер.

В C ++ часто возникают чистые геттеры. Пример в std::ostringstream – это функция str() . Стандартная библиотека часто следует шаблону использования одного и того же имени функции для пары функций getter / setter – str , снова являющейся примером.

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

Кажется, мне нужно подчеркнуть, что сеттерам нужно проверять параметры и применять инварианты, поэтому они обычно не так просты, как они есть здесь. [/редактировать]


Не со всеми, потому что для дополнительной печати. Я часто использую их гораздо чаще, когда Visual Assist дает мне «инкапсулировать поле».

Работа с ногами не больше, если вы реализуете только стандартные сеттеры / геттеры inline в объявлении classа (что я обычно делаю – более сложные сеттеры перемещаются в тело).

Некоторые примечания:

constness: Да, геттер должен быть const. Нельзя использовать возвращаемое значение const, хотя, если вы вернетесь по значению. Для потенциально сложных возвращаемых значений вы можете использовать const & хотя:

 std::string const & GetBar() const { return bar; } 

Цепочка сеттера: многим разработчикам нравится модифицировать сеттер как таковой:

 Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; } 

Это позволяет вызывать множественные сеттеры как таковые:

 Foo foo; foo.SetBar("Hello").SetBaz("world!"); 

Тем не менее, это не универсально принято.

__declspec (свойство) : Visual C ++ предоставляет это нестандартное расширение, чтобы вызывающие могли снова использовать синтаксис свойств. Это немного улучшает работу в classе, но делает код вызывающего абонента более дружелюбным.


Итак, в заключение, есть немного больше проблем с работой, но несколько решений, которые нужно сделать на C ++. Типичный;)

На этом нет строгого соглашения, как в C # или Java. Многие программисты на С ++ просто превратили бы эту переменную в общественность, и это спасло бы самих себя.

Как говорили другие ответы, вам не нужно часто устанавливать и в некоторой степени получать методы.

Но если и когда вы их сделаете, нет необходимости вводить больше, чем необходимо:

 class Foo { public: std::string Bar() const { return bar; } void Bar(const std::string& bar) { this->bar = bar; } private: std::string bar; }; 

Объявление функций inline в classе сохраняет типизацию и намекает компилятору, что вы хотите, чтобы функции были встроены. И это не намного больше, чем эквиваленты C #. Следует отметить, что я удалил префиксы get / set. Вместо этого у нас просто две перегрузки Bar (). Это довольно распространено в C ++ (в конце концов, если он не принимает никаких аргументов, мы знаем, что это getter, и если он принимает аргумент, это сеттер. Нам не нужно имя, чтобы сказать нам это), и это экономит немного больше ввода.

Я почти никогда не использовал геттеры и сеттеры в своем собственном коде. Ответ Вифе выглядит хорошо для меня.

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

 #define GETTER(T,member) const T& Get##member() const { return member; } #define SETTER(T,member) void Set##member(const T & value) { member = value; } class Foo { public: GETTER(std::string, bar) SETTER(std::string, bar) private: std::string bar; } 

Получение и настройка членов данных qua data members: Плохо .
Получение и установка элементов абстракции: Хорошо .

Аргументы против Get / Set с точки зрения дизайна API в банковском примере находятся на месте. Не раскрывайте поля или свойства, если они позволят пользователям нарушать ваши бизнес-правила.

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

Автоматические свойства в c # очень просты в использовании, и существует множество сценариев (привязка данных, сериализация и т. Д.), Которые не работают с полями, но требуют свойств.

Если вы разрабатываете COM-компоненты, то да, это очень популярно.

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

В Eiffel гораздо лучше, когда все, что отличается, – это объем информации, которую вы должны предоставить, чтобы получить ответ – функция с 0 parms такая же, как и доступ к переменной-члену, и вы можете свободно переключаться между ними.

Когда вы управляете обеими сторонами интерфейса, определение интерфейса не похоже на такую ​​большую проблему. Однако, когда вы хотите изменить детали реализации и называет перекомпиляцию клиентского кода, как это обычно бывает на C ++, вы хотите как можно больше свести к минимуму это. Поскольку такие pImpl и get / set будут использоваться больше в публичных API, чтобы избежать такого ущерба.

Методы Get и Set полезны, если у вас есть ограничения в переменной. Например, во многих математических моделях существует ограничение на сохранение определенной переменной float в диапазоне [0,1]. В этом случае Get и Set (специально Set) могут сыграть приятную роль:

 class Foo{ public: float bar() const { return _bar; } void bar(const float& new_bar) { _bar = ((new_bar <= 1) && (new_bar >= 0))?new_bar:_bar; } // Keeps inside [0,1] private: float _bar; // must be in range [0,1] }; 

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

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

Если вы используете C ++ / CLI в качестве вашего varient из C ++, тогда у него есть встроенная поддержка свойств на этом языке, поэтому вы можете использовать

 property String^ Name; 

Это то же самое, что и

 String Name{get;set;} 

в C #. Если вам нужен более точный контроль над методами get / set, вы можете использовать

 property String^ Name { String^ get(); void set(String^ newName); } 

в заголовке и

 String^ ClassName::Name::get() { return m_name; } void ClassName::Name::set(String^ newName) { m_name = newName; } 

в файле .cpp. Я не могу вспомнить, но я думаю, что у вас могут быть разные разрешения доступа для методов get и set (public / private и т. Д.).

Colin

Да, get и set популярны в мире c ++.

Компилятор будет генерировать set_ и get_, если вы определяете свойство, поэтому на самом деле просто сохранить некоторую типизацию.

Это было интересное обсуждение. Это что-то из моей любимой книги «CLR через C #».

Вот что я процитировал.

Лично мне не нравятся свойства, и я хочу, чтобы они не поддерживались в Microsoftm.NET Framework и ее языках программирования. Причина в том, что свойства выглядят как поля, но они являются методами. Это, как известно, вызывает феноменальное количество конфуций. Когда программист видит код, который, как представляется, обращается к полю, существует множество предположений, которые программист делает, что это может быть неверно для свойства. Например,

  • Свойство может быть доступно только для чтения или только для записи; доступ к полям всегда
    читаемый и ansible для записи. Если вы определите
    собственность, лучше всего предложить как
    получить и установить методы доступа.
  • Метод свойств может вызывать исключение; доступ к полю никогда не срабатывает
    исключение.

  • Свойство не может быть передано методу out или ref; поле может.

  • Метод свойства может занять много времени; доступ к полям всегда
    завершается непосредственно. Общий
    причина использования свойств заключается в
    выполнить синхронизацию streamов, которая может остановить stream навсегда, и
    поэтому собственность не должна быть
    используется, если синхронизация streamов
    обязательный. В этой ситуации предпочтительным является метод. Кроме того, если ваш class можно получить удаленно (например,
    ваш class получен из
    System.MashalByRefObject), вызов
    метод свойств будет очень
    медленный, и, следовательно, метод
    предпочтение отдается собственности. В моем
    мнения, classы, полученные из
    MarshalByRefObject никогда не должен использовать
    свойства.

  • Если вы вызываете несколько раз подряд, метод свойств может возвращаться
    другое значение каждый раз;
    поле возвращает одно и то же значение
    время. Класс System.DateTime имеет свойство Now только для чтения, которое возвращает
    текущую дату и время. Каждый раз, когда вы запрашиваете это свойство, он будет
    вернуть другое значение. Это
    ошибки, и Microsoft желает, чтобы
    они могут исправить class, сделав
    Теперь метод вместо свойства.

  • Метод свойств может вызвать наблюдаемые побочные эффекты; доступ к полю никогда не выполняется. Другими словами, пользователь типа должен иметь возможность устанавливать различные
    свойства, определенные типом в любом
    порядок, который он или она выбирает без
    замечая какое-либо другое поведение в
    типа.

  • Метод свойств может потребовать дополнительной памяти или возврата
    ссылка на то, что не
    фактически часть состояния объекта, поэтому изменение возвращаемого объекта
    никакого эффекта на исходный объект;
    запрос поля всегда возвращает
    ссылка на объект, который
    гарантированно быть частью состояния исходного объекта. Работа с
    свойство, которое возвращает копию, может быть
    очень запутанным для разработчиков, и
    эта характеристика часто не документируется.
  • В чем разница между иварами и свойствами в Objective-C
  • Swift readonly external, внутреннее свойство readwrite
  • Сжатие файлов Log4j
  • Когда я должен использовать @synthesize явно?
  • Общаться между двумя windowsми в C #
  • Должен ли я использовать общедоступные свойства и частные поля или публичные поля для данных?
  • Как получить текущее имя свойства через reflection?
  • Как реализовать виртуальные статические свойства?
  • Почему открытые поля быстрее, чем свойства?
  • Перечисление через свойства объекта (строка) в C #
  • Создайте массив свойств целых чисел в Objective C
  • Давайте будем гением компьютера.