Стиль кодирования / компоновщика C ++

Я программировал на C # некоторое время, и теперь я хочу освежить свои навыки на C ++.

Наличие classа:

class Foo { const std::string& name_; ... }; 

Какой был бы лучший подход (я только хочу разрешить доступ для чтения к полю name_):

  • используйте метод getter: inline const std::string& name() const { return name_; } inline const std::string& name() const { return name_; }
  • сделать поле открытым, поскольку он является постоянным

Благодарю.

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

В вашем случае у вас есть поле const, поэтому вышеупомянутые проблемы не являются проблемой. Основной недостаток создания публичного поля заключается в том, что вы блокируете базовую реализацию. Например, если в будущем вы захотите изменить внутреннее представление на C-строку или строку Unicode или что-то еще, тогда вы разделите весь код клиента. С помощью gettor вы можете преобразовать в устаревшее представление для существующих клиентов, предоставляя новые функции новым пользователям через новый gettor.

Я все же предлагаю использовать метод getter, подобный тому, который вы разместили выше. Это максимизирует вашу будущую гибкость.

Использование метода геттера – лучший выбор дизайна для долгоживущего classа, поскольку он позволяет вам заменить метод геттера чем-то более сложным в будущем. Хотя это кажется менее вероятным для значения const, стоимость низкая, а возможные выгоды большие.

В стороне, на C ++, особенно хорошая идея дать и getter, и setter для члена с тем же именем , поскольку в будущем вы можете фактически изменить пару методов:

 class Foo { public: std::string const& name() const; // Getter void name(std::string const& newName); // Setter ... }; 

В одну переменную public member, которая определяет operator()() для каждого:

 // This class encapsulates a fancier type of name class fancy_name { public: // Getter std::string const& operator()() const { return _compute_fancy_name(); // Does some internal work } // Setter void operator()(std::string const& newName) { _set_fancy_name(newName); // Does some internal work } ... }; class Foo { public: fancy_name name; ... }; 

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

В стороне, на C ++, несколько странно иметь член ссылки const. Вы должны назначить его в списке конструкторов. Кому принадлежит фактически память об этом объекте и какова его жизнь?

Что касается стиля, я согласен с остальными, что вы не хотите раскрывать своих рядовых. 🙂 Мне нравится эта модель для сеттеров / геттеров

 class Foo { public: const string& FirstName() const; Foo& FirstName(const string& newFirstName); const string& LastName() const; Foo& LastName(const string& newLastName); const string& Title() const; Foo& Title(const string& newTitle); }; 

Таким образом вы можете сделать что-то вроде:

 Foo f; f.FirstName("Jim").LastName("Bob").Title("Programmer"); 

Я думаю, что теперь подход C ++ 11 будет больше похож на это.

 #include  #include  #include  template class LambdaSetter { public: LambdaSetter() : getter([&]() -> T { return m_value; }), setter([&](T value) { m_value = value; }), m_value() {} T operator()() { return getter(); } void operator()(T value) { setter(value); } LambdaSetter operator=(T rhs) { setter(rhs); return *this; } T operator=(LambdaSetter rhs) { return rhs.getter(); } operator T() { return getter(); } void SetGetter(std::function func) { getter = func; } void SetSetter(std::function func) { setter = func; } T& GetRawData() { return m_value; } private: T m_value; std::function getter; std::function setter; template  friend std::ostream & operator<<(std::ostream &os, const LambdaSetter& p); template  friend std::istream & operator>>(std::istream &is, const LambdaSetter& p); }; template  std::ostream & operator<<(std::ostream &os, const LambdaSetter& p) { os << p.getter(); return os; } template  std::istream & operator>>(std::istream &is, const LambdaSetter& p) { TT value; is >> value; p.setter(value); return is; } class foo { public: foo() { myString.SetGetter([&]() -> std::string { myString.GetRawData() = "Hello"; return myString.GetRawData(); }); myString2.SetSetter([&](std::string value) -> void { myString2.GetRawData() = (value + "!"); }); } LambdaSetter myString; LambdaSetter myString2; }; int _tmain(int argc, _TCHAR* argv[]) { foo f; std::string hi = f.myString; f.myString2 = "world"; std::cout << hi << " " << f.myString2 << std::endl; std::cin >> f.myString2; std::cout << hi << " " << f.myString2 << std::endl; return 0; } 

Я тестировал это в Visual Studio 2013. К сожалению, для того, чтобы использовать базовое хранилище в LambdaSetter, мне нужно было предоставить общеansible accessor «GetRawData», который может привести к нарушению инкапсуляции, но вы можете либо оставить его, либо предоставить свой собственный контейнер для хранения T или просто убедитесь, что единственный раз, когда вы используете «GetRawData» - это когда вы пишете пользовательский метод getter / setter.

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

 class Foo { public: const std::string& getName() const {return name_;} private: const std::string& name_; }; 

Обратите внимание: если вы хотите изменить getName() чтобы вернуть вычисленное значение, он не сможет вернуть const ref. Это нормально, потому что это не потребует каких-либо изменений для вызывающих (по модулю перекомпиляции).

Избегайте общедоступных переменных, за исключением classов, которые по существу являются структурами C-стиля. Это не очень хорошая практика.

После того как вы определили интерфейс classа, вы никогда не сможете его изменить (кроме добавления к нему), потому что люди будут использовать его и полагаться на него. Создание переменной public означает, что вам нужно иметь эту переменную, и вам нужно убедиться, что она имеет то, что нужно пользователю.

Теперь, если вы используете геттер, вы обещаете предоставить некоторую информацию, которая в настоящее время хранится в этой переменной. Если ситуация изменится, и вы не хотите постоянно поддерживать эту переменную, вы можете изменить ее. Если требования меняются (и я видел некоторые довольно странные изменения требований), и вам в основном нужно имя, которое находится в этой переменной, но иногда одно в этой переменной, вы можете просто изменить getter. Если вы сделаете переменную общедоступной, вы застряли бы с ней.

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

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

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

Из теории проектных шаблонов; «инкапсулировать то, что меняется». Определяя «геттер», существует хорошая приверженность указанному выше принципу. Итак, если реализация-представление участника изменится в будущем, член может быть «массирован» перед возвратом из «getter»; подразумевая отсутствие рефакторинга кода на стороне клиента, где сделан запрос «getter».

С Уважением,

Interesting Posts

Android – распаковать папку?

Скопируйте все установленные программы и файлы на жесткий диск (который имеет 32-битную Windows 7) и клонируйте / перенесите его на другой компьютер с 64-разрядной версией Windows 7

Tomcat 8 позволяет вести отладочную запись для enums ненужных банок

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

iPhone UINavigation Issue – вложенная анимация push может привести к повреждению навигационной панели

HQL присоединился к запросу, чтобы получить большое количество связей

Почему Java разрешает массивы размером 0?

objective ActionName

Разрешены ли повторяющиеся ключи в определении двоичных деревьев поиска?

Как изменить Windows 7 для нормальной загрузки, а не в безопасном режиме?

Что мешает переписывать CD-R?

Раздельные сборки ‘debug’ и ‘release’?

Как создать задержку в Swift?

Отключить от доли samba

Сохранение контекстного меню правой кнопкой мыши

Давайте будем гением компьютера.