Урок №114. Спецификаторы доступа public и private

  Юрий  | 

    | 

  Обновл. 11 Июн 2019  | 

 14210

 ǀ   14 

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

Спецификаторы доступа

Рассмотрим следующую программу:

Здесь мы объявляем структуру DateStruct, а затем напрямую обращаемся к её членам для их инициализации. Это работает, так как все члены структуры являются открытыми по умолчанию. Открытые члены (или ещё «члены public») — это члены структуры или класса, к которым можно получить доступ извне этой же структуры или класса. В программе выше функция main() находится вне структуры, но она может напрямую обращаться к членам day, month и year, так как они являются открытыми.

С другой стороны, рассмотрим следующий почти идентичный класс:

Вам бы не удалось скомпилировать эту программу, так как все члены класса являются закрытыми по умолчанию. Закрытые члены (или ещё «члены private») — это члены класса, доступ к которым имеют только другие члены этого же класса. Поскольку функция main() не является членом DateClass, то она и не имеет доступа к закрытым членам объекта date.

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

Поскольку теперь члены класса DateClass являются открытыми, то к ним можно получить доступ напрямую из функции main().

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

В C++ есть 3 уровня доступа:

   public: делает члены открытыми;

   private: делает члены закрытыми;

   protected: открывает доступ к членам только для дружественных и дочерних классов (детальнее об этом в соответствующем уроке).

Использование спецификаторов доступа


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

Правило: Устанавливайте спецификатор доступа private переменным-членам класса и спецификатор доступа public методам класса (если у вас нет веских оснований делать иначе).

Рассмотрим пример класса, который использует спецификаторы доступа private и public:

Результат выполнения программы выше:

12/11/2018

Обратите внимание, хоть мы и не можем получить доступ к переменным-членам объекта date напрямую из main() (так как они являются private по умолчанию), мы можем получить доступ к ним через открытые методы setDate() и print()!

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

Некоторые программисты предпочитают сначала перечислять члены private, а затем уже public, так как члены public обычно используют члены private (те же переменные-члены в методах класса), поэтому имеет смысл сначала определять private, а затем уже public члены. Другие же программисты считают, что сначала нужно указывать члены public, так как члены private закрыты, получать к ним доступ напрямую нельзя, то и выносить их на первое место тоже не нужно. Работать будет и так, и сяк. Какой способ использовать — выбирайте сами, что вам удобнее.

Рассмотрим следующую программу:

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

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

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

Структуры vs. Классы

Теперь, когда мы узнали о спецификаторах доступа, мы можем говорить о фактических различиях между классом и структурой в C++. Класс по умолчанию устанавливает всем своим членам спецификатор доступа private. Структура же по умолчанию устанавливает всем своим членам спецификатор доступа public.

Вот так вот!

(Есть ещё одно незначительное отличие: структуры наследуют от других классов открыто, а классы наследуют закрыто. Мы рассмотрим это детальнее в следующей главе).

Тест


Задание №1

a) Что такое открытый член?

Ответ 1.а)

Открытый член — это член класса, доступ к которому имеют объекты как внутри, так и извне класса.

b) Что такое закрытый член?

Ответ 1.b)

Закрытый член — это член класса, доступ к которому имеют только другие члены этого же класса.

c) Что такое спецификатор доступа?

Ответ 1.c)

Спецификатор доступа определяет, кто имеет доступ к членам этого же спецификатора.

d) Сколько есть спецификаторов доступа в C++? Назовите их.

Ответ 1.d)

В С++ есть 3 спецификатора доступа:

   public;

   private;

   protected.

Задание №2

a) Напишите простой класс с именем Numbers. Этот класс должен иметь:

   Три закрытые переменные-члены типа double: m_a, m_b и m_c.

   Открытый метод с именем setValues(), который позволит устанавливать значения для m_a, m_b и m_c;

   Открытый метод с именем print(), который будет выводить объект класса Numbers в следующем формате: <m_a, m_b, m_c>.

Следующий код функции main():

Должен производить следующий результат:

<3, 4, 5>

Ответ 2.а)

b) Добавьте функцию isEqual() в класс Numbers, чтобы следующий код работал корректно:

Ответ 2.b)

Задание №3

Теперь попробуем что-то посложнее. Напишите класс, который реализует функционал стека. Класс Stack должен иметь:

   Открытый целочисленный фиксированный массив длиной 10.

   Открытое целочисленное значение для отслеживания длины стека.

   Открытый метод с именем reset(), который будет сбрасывать длину и все значения элементов на 0.

   Открытый метод с именем push(), который будет добавлять значение в стек. Метод push() должен возвращать значение false, если массив уже заполнен, в противном случае — true.

   Открытый метод с именем pop() для возврата значений из стека. Если в стеке нет значений, то должен выводиться стейтмент assert.

   Открытый метод с именем print(), который будет выводить все значения стека.

Следующий код функции main():

Должен производить следующий результат:

( )
( 3 7 5 )
( 3 7 )
( )

Ответ №3

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (56 оценок, среднее: 4,86 из 5)
Загрузка...

Комментариев: 14

  1. Аватар Анатолий:

    Если в классе сначала идут все private члены, а потом public, обязательно ли явно указывать спецификатор privatе? В классе ведь всем членам по умолчанию устанавливается спецификатор доступа private.

    1. Аватар Ануар:

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

      1. Аватар Анатолий:

        В некоторых дальнейших уроках фигурирует код с принудительным указанием private, например итоговый тест этой главы второе задание. Это имеет какую то причину, или так совпали звёзды? ))

        1. Аватар Ануар:

          так совпали звезды

  2. Аватар Ануар:

    Я тут недавно пытался написать программу в одном компиляторе и он заставлял меня в классах везде прописывать const int blabla вместо int blabla , почему ?

  3. Аватар Игорь:

    Мой вариант 3 задания

  4. Аватар Михаил:

    В задании 2 переменные имеют тип double и сравниваются между собой. Корректно ли это? Например такие не сравниваются (наобум набрал)

  5. Аватар Anastasia:

    Что-то я забыла про значение для отслеживания длины стека и написала код без него.
    Корректно получилось?

  6. Аватар Sergey:

    И ещё возник вопрос, правильно ли у меня работает assert hello-site.ru/share/Stek — код программы. priscree.ru/img/ddd58dea7702d7.jpg — скриншот проблемы. Приходится закрывать всплывающее окно, не похоже на нормальную работу программы…

    1. Юрий Юрий:

      У вас код идентичен с кодом ответа (только другие имена переменных). Всё правильно. Появление ошибки — это нормальное срабатывание стейтмента assert. Assert у вас срабатывает, так как вы вызываете метод pop, прежде чем заполняете стек данными:

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

  7. Аватар Sergey:

    Есть небольшая недоработка в ответе задания 3, в разделе private класса Stack, нужно инициализировать индекс свободного элемента стека в 0. (На случай, если кто-то сразу захочет вызвать метод pop)

    1. Юрий Юрий:

      Так ведь используется стейтмент assert, который выведет сообщение об ошибке, если кто-то попытается вызвать метод pop сразу. Зачем инициализировать нулём?

      1. Аватар Евгений:

        Добрый день! МЕня вот какой момент заинтересовал:

        Почему С++ вначале присваивает значение value для нулевого элемента, а потом уже только увеличивает значение m_next++ на единицу? Пытаюсь связать с 38 уроком, но не получается понять порядок действий с++ с этим аргументом.

    2. Аватар Nikita:

      Для этих целей в программе предусмотрена функция reset(), которая присваивает длине значение = 0. Ваше утверждение верно, если мы не будем использовать этот метод

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *