

Should you ever use protected member variables?
Зависит от того, насколько вы придирчивы к скрытию состояния.
Если разработчик придет и создаст подклассы вашего класса, он может испортить его, потому что не понимает его полностью. С закрытыми членами, кроме открытого интерфейса, они не могут видеть детали реализации того, как что-то делается, что дает вам возможность изменить это позже.
Я бы сказал, что это не то, о чем стоит беспокоиться, если вы не обнаружите при профилировании, что горлышко бутылки в конечном итоге является аксессуаром (чего почти никогда не бывает). Есть уловки, которые можно использовать, чтобы сделать JIT умнее, если это окажется проблемой. Например, в java вы можете намекнуть, что аксессор может быть встроен, пометив его как final. Хотя, честно говоря, производительность геттеров и сеттеров гораздо менее важна, чем работа с организацией системы и фактическими проблемами производительности, определяемыми профилировщиком.
@Jake: Никогда не принимайте проектные решения, основываясь на предположениях о производительности. Вы принимаете дизайнерские решения, основываясь на том, что вы считаете лучшим дизайном, и только если реальное профилирование показывает узкое место в вашем дизайне, вы идете и исправляете его. Обычно, если дизайн хороший, производительность тоже хорошая.
@Black Очевидно, что Allain имел в виду «они не могут доступ» этих членов и, следовательно, не могут создавать код против них, оставляя автору класса возможность удалить / изменить защищенные члены позже. (Конечно, идиома pimpl позволит скрыть их визуально, а также от единиц перевода, включая заголовок.)
Для получения подробной информации о модификаторах доступа .Net иди сюда
У защищенных переменных-членов нет реальных преимуществ или недостатков, вопрос в том, что вам нужно в вашей конкретной ситуации. Обычно принято объявлять переменные-члены как частные и разрешать внешний доступ через свойства. Кроме того, некоторые инструменты (например, некоторые преобразователи O / R) ожидают, что данные объекта будут представлены свойствами, и не распознают общедоступные или защищенные переменные-члены. Но если вы знаете, что хотите, чтобы ваши подклассы (и ТОЛЬКО ваши подклассы) имели доступ к определенной переменной, нет причин не объявлять ее защищенной.
Желание подклассов к доступ переменной сильно отличается от желания, чтобы они могли свободно ее мутировать. Это один из основных аргументов против защищенных переменных: теперь ваш базовый класс не может предполагать, что какой-либо из его инвариантов выполняется, потому что любой производный класс может делать с защищенными членами абсолютно все, что угодно. Это главный аргумент против них. Если им просто нужно доступ данных, тогда ... напишите средство доступа. : P (Я использую защищенные переменные, хотя, вероятно, больше, чем следовало бы, и я постараюсь сократить их количество!)
В настоящее время принято считать, что они вызывают чрезмерную связь между производными классами и их базами.
У них нет особого преимущества перед защищенными методами / свойствами (когда-то у них могло быть небольшое преимущество в производительности), и они также чаще использовались в эпоху, когда в моде было очень глубокое наследование, чего сейчас нет.
Разве no particular advantage over protected methods/properties не должен быть no particular advantage over *private* methods/properties?
Нет, потому что я / говорил о преимуществах / недостатках различных способов связи между производными классами и их базами - все эти методы будут `` защищены '' - разница в том, являются ли они переменными-членами (полями) или свойствами / методами ( т.е. какие-то подпрограммы).
Спасибо за быстрое разъяснение. Я рад получить через час оригинальный ответ автора на мой вопрос к посту 6-летней давности. Вы не думаете, что такое может случиться на большинстве других онлайн-форумов :)
Еще более примечательно то, что я действительно согласен с самим собой в течение этого времени ...
Одна из задач конструктора - следить за тем, чтобы все переменные состояния были явно инициализированы. Если вы придерживаетесь этого соглашения, вы можете использовать конструкцию super для вызова родительского конструктора; Затем он позаботится об инициализации переменных частного состояния в родительском классе.
Короче да.
Защищенные переменные-члены позволяют получить доступ к переменной из любых подклассов, а также из любых классов в одном пакете. Это может быть очень полезно, особенно для данных только для чтения. Однако я не верю, что они когда-либо понадобятся, потому что любое использование защищенной переменной-члена может быть реплицировано с помощью частной переменной-члена и пары геттеров и сеттеров.
И наоборот, частные переменные-члены также никогда не нужны; public достаточно для любого использования.
Обычно, если что-то намеренно не рассматривается как публичное, я делаю это частным.
Если возникает ситуация, когда мне нужен доступ к этой частной переменной или методу из производного класса, я меняю ее с частной на защищенную.
Такого почти никогда не бывает - я вообще-то не фанат наследования, так как это не очень хороший способ моделировать большинство ситуаций. В любом случае, не беспокойтесь.
Я бы сказал, что это нормально (и, вероятно, лучший способ сделать это) для большинства разработчиков.
Простой факт в том, что, если через год придет другой разработчик и решит, что ему нужен доступ к вашей частной переменной-члену, он просто отредактирует код, изменит его на защищенный и продолжит свой бизнес.
Единственное реальное исключение из этого - если вы занимаетесь доставкой двоичных dll в форме черного ящика третьим лицам. В основном это Microsoft, поставщики «Custom DataGrid Control» и, возможно, несколько других крупных приложений, которые поставляются с библиотеками расширяемости. Если вы не относитесь к этой категории, не стоит тратить время / силы на то, чтобы беспокоиться о подобных вещах.
В большинстве случаев использовать 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. */ }
Я использовал такой код менее месяца назад (так что код взят из памяти). Поразмыслив, я считаю, что, хотя общий контейнер Base должен быть абстрактным классом, даже если он может жить достаточно хорошо, потому что использование напрямую Base было бы такой болью, что его следует запретить.
Резюме Таким образом, вы защитили данные, используемые производным классом. Тем не менее, мы должны учитывать тот факт, что базовый класс должен быть абстрактным.
он не нарушает инкапсуляцию больше, чем публичные члены. Это параметр, указывающий, что производные классы могут использовать состояние класса, которое не предоставляется пользователям класса.
@gbjbaanb: вы противоречите себе: «это не нарушает инкапсуляцию больше, чем публичные члены» отличается от «[только] производные классы могут использовать состояние класса». «защищенный» - это что-то среднее между публичным и частным. Так что "protected [...] несколько нарушает инкапсуляцию" все еще верно ...
на самом деле, в языке C++ адаптеры контейнера, такие как std :: stack, будут открывать базовый объект контейнера с защищенной переменной с именем «c».
Я знаю, что этот пост довольно старый, но я чувствую необходимость вмешаться. Вы не "несколько" нарушаете инкапсуляцию, вы полностью ее нарушаете. protected не более инкапсулирован, чем public. Я хочу доказать свою неправоту. Все, что вам нужно сделать, это написать класс с защищенным членом и запретить мне изменять его. Очевидно, что класс не должен быть окончательным, поскольку весь смысл использования protected - наследование. Либо что-то инкапсулировано, либо нет. Промежуточного состояния нет.
В общем, я бы сохранил ваши защищенные переменные-члены в редких случаях, когда вы полностью контролируете код, который их также использует. Если вы создаете публичный API, я бы сказал никогда. Ниже мы будем называть переменную-член «свойством» объекта.
Вот что делает ваш суперкласс не можешь после того, как переменная-член защищена, а не закрыта с аксессорами:
лениво создавать значение на лету, когда свойство читается. Если вы добавите защищенный метод получения, вы можете лениво создать значение и передать его обратно.
знать, когда свойство было изменено или удалено. Это может привести к ошибкам, когда суперкласс делает предположения о состоянии этой переменной. Создание защищенного метода установки для переменной сохраняет этот контроль.
Установите точку останова или добавьте вывод отладки при чтении или записи переменной.
Переименуйте эту переменную-член, не просматривая весь код, который может ее использовать.
В общем, я думаю, что это был бы редкий случай, когда я бы порекомендовал создать защищенную переменную-член. Лучше потратить несколько минут на раскрытие свойства через геттеры / сеттеры, чем спустя несколько часов, отслеживая ошибку в каком-либо другом коде, который изменил защищенную переменную. Более того, вы застрахованы от добавления функций в будущем (например, отложенной загрузки) без нарушения зависимого кода. Позже сделать это сложнее, чем сейчас.
На уровне дизайна может быть уместно использовать защищенное свойство, но для реализации я не вижу преимуществ в сопоставлении его с защищенной переменной-членом, а не с методами доступа / мутатора.
Защищенные переменные-члены имеют существенные недостатки, поскольку они эффективно разрешают клиентскому коду (подклассу) доступ к внутреннему состоянию класса базового класса. Это мешает базовому классу эффективно поддерживать свои инварианты.
По той же причине защищенные переменные-члены также значительно затрудняют написание безопасного многопоточного кода, если только не гарантируется константа или не ограничивается одним потоком.
Методы аксессоров / мутаторов предлагают значительно большую стабильность API и гибкость реализации при обслуживании.
Кроме того, если вы сторонник объектно-ориентированного подхода, объекты взаимодействуют / взаимодействуют путем отправки сообщений, а не чтения / настройки состояния.
Взамен они предлагают очень мало преимуществ. Я не обязательно удаляю их из чужого кода, но сам я ими не пользуюсь.
Для протокола: в пункте 24 «Исключительного C++» в одной из сносок Саттер идет «вы бы никогда не написали класс, у которого есть общедоступная или защищенная переменная-член. верно? (Несмотря на плохой пример, установленный некоторыми библиотеками.)»
Ключевая проблема для меня заключается в том, что после того, как вы сделаете переменную защищенной, вы не сможете разрешить любому методу в вашем классе полагаться, если его значение находится в пределах диапазона, потому что подкласс всегда может поместить его за пределы диапазона.
Например, если у меня есть класс, который определяет ширину и высоту визуализируемого объекта, и я делаю эти переменные защищенными, тогда я не могу делать никаких предположений относительно (например) соотношения сторон.
Что критически важно, я могу никогда делать эти предположения в любой момент с момента, когда этот код выпущен в виде библиотеки, поскольку даже если я обновлю свои сеттеры для поддержания соотношения сторон, у меня нет гарантии, что переменные устанавливаются через сеттеры или доступны через геттеры в существующем коде.
Ни один из подклассов моего класса не может сделать эту гарантию, так как они также не могут обеспечить соблюдение значений переменных, даже если в этом весь смысл их подкласса.
В качестве примера:
Ограничивая переменные закрытыми, я могу затем обеспечить желаемое поведение через сеттеры или геттеры.
Можете ли вы прокомментировать производительность защищенных переменных по сравнению с частной переменной с помощью метода get / set?