Public в c что это
Все поля, методы и остальные компоненты класса имеют модификаторы доступа . Модификаторы доступа позволяют задать допустимую область видимости для компонентов класса. То есть модификаторы доступа определяют контекст, в котором можно употреблять данную переменную или метод.
В языке C# применяются следующие модификаторы доступа:
private : закрытый или приватный компонент класса или структуры. Приватный компонент доступен только в рамках своего класса или структуры.
private protected : компонент класса доступен из любого места в своем классе или в производных классах, которые определены в той же сборке.
file : добавлен в версии C# 11 и применяется к типам, например, классам и структурам. Класс или структура с такми модификатором доступны только из текущего файла кода.
protected : такой компонент класса доступен из любого места в своем классе или в производных классах. При этом производные классы могут располагаться в других сборках.
internal : компоненты класса или структуры доступен из любого места кода в той же сборке, однако он недоступен для других программ и сборок.
protected internal : совмещает функционал двух модификаторов protected и internal . Такой компонент класса доступен из любого места в текущей сборке и из производных классов, которые могут располагаться в других сборках.
public : публичный, общедоступный компонент класса или структуры. Такой компонент доступен из любого места в коде, а также из других программ и сборок.
Стоит отметить, что эти модификаторы могут применяться как к компонентам класса, так и к компонентам структуры за тем исключением, что структуры не могут использовать модификаторы private protected , protected и protected internal , поскольку структуры не могут быть унаследованы.
Все классы и структуры, определенные напрямую вне других типов (классов и структур) могут иметь только модификаторы public , file или internal .
Мы можем явно задать модификатор доступа, а можем его и не указывать:
Если для компонентов не определен модификатор доступа, то по умолчанию для них применяется модификатор private . Например, в примере выше переменная name неявно будет иметь модификатор private .
Классы и структуры, которые объявлены без модификатора и которые расположены вне других типов, по умолчанию имеют доступ internal , а вложенные классы и структуры, как и остальные компоненты классов/структур имеют модификатор private . Например:
Здесь класс Phone не является вложенным ни в один другой класс/структуру, поэтому неявно имеет модификатор internal . А структура Camera является вложенной, поэтому, как и другие компоненты класса, неявно имеет модификатор private
Модификаторы в рамках текущего проекта
Посмотрим на примере и создадим следующий класс State:
Так как класс State не имеет явного модификатора, по умолчанию он имеет модификатор internal , поэтому он будет доступен из любого места данного проекта, однако не будет доступен из других программ и сборок.
Класс State имеет шесть полей для каждого уровня доступа. Плюс одна переменная без модификатора, которая является закрытой (private) по умолчанию. А также определено семь методов с разными модификаторами, которые выводят значения соответствующих переменных на консоль. Поскольку все модификаторы позволяют использовать компоненты класса внутри данного класса, то и все переменные класса, в том числе закрытые, у нас доступны всем его методам, так как все находятся в контексте класса State.
Теперь посмотрим, как мы сможем использовать переменные класса State в другом классе, который, допустим, будет называться StateConsumer и который расположен в том же проекте :
Таким образом, в классе StateConsumer мы смогли только обратиться к переменным internalVar, protectedInternalVar и publicVar, так как их модификаторы позволяют использовать в данном контексте.
Аналогично дело обстоит и с методами:
Здесь нам оказались доступны только три метода: PrintInternal, PrintProtectedInternal, PrintPublic, которые имееют соответственно модификаторы internal, protected internal, public.
Модификаторы в рамках сборок
Допустим, у нас есть проект (и соответственно сборка) MyLib, в которой определены три класса:
Здесь классы DefaultState и InternalState имеют модификатор internal , поэтому доступны только в текущем проекте.
Класс PublicState модификатором public доступен из других проектов. Однако его метод PrintInternal() доступен только в текущем проекте. Вне текущего проекта доступен только его метод PrintPublic и PrintProtectedInternal() (доступен в другом проекте только в классах-наследниках).
Допустим, мы подключаем сборку этого проекта MyLib в другой проект, где есть класс StateConsumer:
В классе StateConsumer есть доступ только к классу PublicState и его методу PrintPublic , потому что они имеют модификатор public . К остальной функциональности подключенной сборки StateConsumer доступа не имеет.
Благодаря такой системе модификаторов доступа можно скрывать некоторые моменты реализации класса от других частей программы.
Файл как область видимости
C# 11 добавил еще один модификатор видимости — file , который с применяется к классам, структурам, делегатам, перечислениям, интерфейсам. Типы с этим модификаторо могут использоваться только внутри текущего файла кода.
Данный модификатор не может использоваться в паре с другими модификаторами.
12.3 – Спецификаторы доступа public и private
В этой программе мы объявляем переменную типа DateStruct , а затем напрямую обращаемся к ее членам, чтобы инициализировать их. Это работает, потому что все члены структуры по умолчанию являются открытыми. Открытые ( public ) члены – это члены структуры или класса, к которым можно получить доступ извне структуры или класса. В этом случае функция main() находится вне структуры, но она может напрямую обращаться к членам month , day и year , поскольку они являются открытыми.
С другой стороны, рассмотрим следующий почти идентичный класс:
Если бы вы попытались скомпилировать эту программу, вы бы получили ошибки. Это связано с тем, что по умолчанию все члены класса являются закрытыми. Закрытые ( private ) члены – это члены класса, к которым могут получить доступ только другие члены класса. Поскольку main() не является членом DateClass , у нее нет доступа к закрытым членам date .
Спецификаторы доступа
Хотя члены класса по умолчанию являются закрытыми, с помощью ключевого слова public мы можем сделать их открытыми:
Поскольку члены DateClass теперь открыты, к ним можно получить доступ напрямую через main() .
Ключевое слово public вместе с последующим двоеточием называется спецификатором доступа. Спецификаторы доступа определяют, у кого есть доступ к членам, которые следуют за спецификатором. Каждый из членов «получает» уровень доступа предыдущего спецификатора доступа (или, если он не указан, спецификатора доступа по умолчанию).
C++ предоставляет 3 ключевых слова для различных спецификаторов доступа: public , private и protected . public и private используются для того, чтобы сделать членов, которые следуют за ними, открытыми или закрытыми соответственно. Третий спецификатор доступа, protected , работает так же, как и private . Мы обсудим разницу между спецификаторами доступа private и protected при рассмотрении наследования.
Смешивание спецификаторов доступа
Класс может (и почти всегда так делает) использовать несколько спецификаторов доступа для установки уровней доступа каждому из своих членов. Ограничений на количество спецификаторов доступа, которые вы можете использовать в классе, нет.
Как правило, переменные-члены обычно делаются закрытыми, а функции-члены обычно становятся открытыми. В следующем уроке мы подробнее рассмотрим, почему.
Правило
Делайте переменные-члены закрытыми, а функции-члены открытыми, если у вас нет веской причины поступать иначе.
Давайте посмотрим на пример класса, который использует как спецификаторы доступа, как private , так и public :
Эта программа печатает:
Обратите внимание, что хотя мы не можем получить доступ к переменным-членам date m_month , m_day и m_year непосредственно из main (поскольку они являются закрытыми), мы можем получить к ним доступ косвенно через открытые функции-члены setDate() и print() !
Группа открытых членов класса часто называется открытым интерфейсом. Поскольку только открытые члены могут быть доступны извне класса, открытый интерфейс определяет, как программы, использующие класс, будут взаимодействовать с этим классом. Обратите внимание, что main() ограничена установкой даты и печатью даты. Класс защищает переменные-члены от прямого доступа или редактирования.
Некоторые программисты предпочитают сначала перечислять закрытые члены, потому что открытые члены обычно используют закрытые, поэтому имеет смысл сначала определить закрытые. Однако хороший контраргумент состоит в том, что пользователям класса не важны закрытые члены, поэтому открытые должны быть на первом месте. Хорош любой из этих подходов.
Контроль доступа работает на основе класса
Рассмотрим следующую программу:
Один нюанс C++, который часто упускают или неправильно понимают, заключается в том, что контроль доступа работает на основе класса, а не на основе объекта. Это означает, что когда функция имеет доступ к закрытым членам класса, она может получить доступ к закрытым членам любого объекта этого типа класса, который она может видеть.
В приведенном выше примере copyFrom() является членом DateClass , что дает ей доступ к закрытым членам DateClass . Это означает, что copyFrom() может напрямую обращаться не только к закрытым членам неявного объекта, с которым она работает, но также означает, что она имеет прямой доступ к закрытым членам DateClass параметра d ! Если бы параметр d был другого типа, этого не было бы.
Это может быть особенно полезно, когда нам нужно скопировать члены из одного объекта класса в другой объект того же класса. Мы также увидим, что эта тема снова всплывет, когда в следующей главе мы будем говорить о перегрузке operator<< для печати членов класса.
Снова о структурах и классах
Теперь, когда мы поговорили о спецификаторах доступа, мы можем поговорить о реальных различиях между классом и структурой в C++. Класс по умолчанию делает своих членов закрытыми. Структура по умолчанию делает свои члены открытыми.
(Хорошо, если быть педантичным, есть еще одно незначительное отличие – структуры наследуются от других классов открытым (public) образом, а классы наследуются закрытым (private) образом. Мы рассмотрим, что это означает, в следующей главе, но этот конкретный момент практически не имеет значения, так как в любом случае вы никогда не должны полагаться на значения по умолчанию).
Небольшой тест
Вопрос 1
a) Что такое открытый член?
Открытый член – это член класса, к которому может получить доступ кто угодно.
b) Что такое закрытый член?
Закрытый член — это член класса, к которому могут получить доступ только другие члены класса.
c) Что такое спецификатор доступа?
Спецификатор доступа определяет, кто имеет доступ к членам, которые следуют за спецификатором.
d) Сколько существует спецификаторов доступа, и какие они?
Три. public , private и protected .
Вопрос 2
a) Напишите простой класс с именем Point3d . Класс должен содержать:
- три закрытых переменных-члена типа int с именами m_x , m_y и m_z ;
- открытую функцию-член с именем setValues() , которая позволяет вам устанавливать значения для m_x , m_y и m_z ;
- открытую функцию-член с именем print() , которая печатает Point3d в следующем формате: <m_x, m_y, m_z> .
Убедитесь, что следующая программа выполняется правильно:
Она должна напечатать:
b) Добавьте функцию с именем isEqual() в свой класс Point3d . Следующий код должен работать правильно:
Вопрос 3
А теперь давайте попробуем кое-что посложнее. Давайте с нуля напишем класс, реализующий простой стек. Если вам нужно напомнить, что такое стек, просмотрите урок «11.8 – Стек и куча».
Класс должен называться Stack и содержать:
- закрытый фиксированный массив чисел int длиной 10.
- закрытое число int для отслеживания размера стека.
- открытая функция-член с именем reset() , которая устанавливает размер равным 0.
- открытая функция-член с именем push() , которая помещает значение в стек. push() должна возвращать false , если массив уже заполнен, и true в противном случае.
- открытая функция-член с именем pop() , которая извлекает значение из стека и возвращает его. Если в стеке нет значений, программа должна завершиться через assert .
- открытая функция-член с именем print() , которая печатает все значения в стеке.
Убедитесь, что следующая программа выполняется правильно:
Эта программа должна напечатать:
Совет
Вместо того, чтобы писать собственную реализацию стека каждый раз, когда он вам нужен, используйте std::stack . Подобно std::array и std::vector , вы можете указать тип элемента при его создании.