Следует ли использовать защищенные переменные-члены?

следует ли использовать защищенные переменные-члены? Какие преимущества и какие проблемы это может вызвать?

10 ответов


следует ли использовать защищенные переменные-члены?

зависит от того, насколько вы придирчивы к скрытию состояния.

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

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


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

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


вообще, если что-то не намеренно воспринимается как публичное, я делаю это частным.

Если возникает ситуация, когда мне нужен доступ к этой частной переменной или методу из производного класса, я меняю его с private на protected.

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

Я бы сказал, что это нормально (и, вероятно, лучший способ to go about it) для большинства разработчиков.

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

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


В общем, я бы сохранил ваши защищенные переменные-члены в редком случае, когда у вас есть полный контроль над кодом, который их использует. Если вы создаете публичный API, я бы сказал Никогда. Ниже мы будем ссылаться на переменную-член как на "свойство" объекта.

вот что ваш суперкласс не может do после создания переменной-члена защищенной, а не частной-с-аксессорами:

  1. лениво создайте значение на лету, когда свойство читается. Если вы добавляете защищенный метод getter, вы можете лениво создать значение и передать его обратно.

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

  3. установка точки останова или добавление вывода отладки при чтении или записи переменной к.

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

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


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

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

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

методы Accessor/mutator предлагают значительно больше стабильности API и гибкости внедрения при обслуживании.

кроме того, если вы пурист OO, объекты сотрудничают/общаются путем отправки сообщений, а не чтения/настройки состояния.

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


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

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

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

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

Как пример:

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

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


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

но у меня есть один хороший пример: предположим, что какой-то общий контейнер. Он имеет внутреннюю реализацию и внутренние аксессоры. Но вам нужно предложить как минимум 3 открытых доступа к его данным: map, hash_map, vector-like. Тогда у вас есть что-то вроде:

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

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

резюме таким образом, вы защитили данные, используемые в производном классе. Тем не менее, мы должны учитывать тот факт, что базовый класс должен быть абстрактным.


короче, да.

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


для подробной информации о .Net access modifiers иди сюда

нет никаких реальных преимуществ или недостатков в защищенные переменные-члены, это вопрос о том, что вам нужно в вашей конкретной ситуации. В целом принято объявлять переменные-члены закрытыми и разрешать внешний доступ через свойства. Кроме того, некоторые инструменты (например, некоторые O/R mappers) ожидают, что данные объекта будут представлены свойствами и не распознают открытый или защищенный элемент переменная. Но если вы знаете, что хотите, чтобы ваши подклассы (и только ваши подклассы) имели доступ к определенной переменной, нет причин не объявлять ее защищенной.


просто для записи, под пунктом 24 " исключительного C++", в одной из сносок Саттер идет "вы никогда не напишете класс, который имеет общедоступную или защищенную переменную-член. правильно? (Независимо от плохого примера, заданного некоторыми библиотеками.)"