Menu

Спецификаторы доступа в C++: public, private, protected

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

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Кто имеет право трогать ваши данные

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

Их ровно три: public, private и protected. Каждый помечает следующие за ним члены, и метка решает, какому коду разрешено их читать или записывать. Сделаете правильно — ваш класс сам обеспечивает свои правила; сделаете неправильно — любая ошибка где угодно может повредить состояние вашего объекта.

Три спецификатора

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

Раскомментируйте последнюю строку — и компилятор откажется собирать: balance является private, поэтому main не может трогать его напрямую. В этом и смысл: единственный способ изменить баланс — через deposit, а значит, вы сможете позже добавить проверку (никаких отрицательных вкладов, логирование, лимиты) в одном месте и быть уверены, что она всегда применяется.

Вот полная разбивка:

//             доступно из...
// public      откуда угодно (любой код, у которого есть объект)
// private     только собственные члены класса (+ friends)
// protected   собственные члены класса И производные классы (+ friends)

class против struct: значение по умолчанию

Вы можете писать сколько угодно блоков спецификаторов в любом порядке. То, что члены получают до того, как вы напишете первый, зависит от того, использовали ли вы class или struct:

  • В class члены по умолчанию private.
  • В struct члены по умолчанию public.

Это значение по умолчанию — единственное отличие на уровне языка между двумя ключевыми словами. У struct могут быть методы, конструкторы и private-секции точно так же, как у class.

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

Инкапсуляция с геттерами и сеттерами

Повседневный приём таков: данные помещают в private-секцию, а public-метод даёт контролируемый доступ. Геттер только для чтения возвращает значение; сеттер проверяет перед присваиванием. Именно здесь private окупается.

Поскольку celsius является private, протащить некорректное значение невозможно — каждая запись должна пройти через setCelsius, который охраняет инвариант. Обратите внимание, что геттеры помечены const: они обещают не изменять объект, поэтому их можно вызывать и на объектах const Temperature.

protected и наследование

protected имеет значение только тогда, когда в дело вступает наследование. Для внешнего кода он ведёт себя как private, но производный класс может до него добраться. Используйте его для членов, которые законно нужны подклассу, но к которым публике всё равно не следует обращаться.

Частая ошибка новичков — хвататься за protected на каждом члене данных «на случай, если подклассу понадобится». Это тихо расширяет контракт вашего класса — теперь каждый подкласс может зависеть от этого поля, и вы не можете менять его свободно. Предпочитайте private и повышайте до protected только тогда, когда производному классу действительно нужен доступ.

Лазейка friend

Иногда одной внешней функции или классу законно нужно видеть ваши внутренности — классический случай — оператор вроде <<, который нельзя сделать членом. Ключевое слово friend предоставляет этой одной названной сущности доступ к вашим членам private и protected, и ничему больше.

friend — это намеренное, точечное исключение: сам класс называет, кому именно он доверяет, поэтому никто не может предоставить себе доступ извне. Используйте его умеренно; если вы ловите себя на добавлении множества friends, ваши члены, вероятно, изначально не должны были быть private, либо ваш дизайн нуждается в переосмыслении.

Распространённые ошибки, которых стоит избегать

  • Делать все члены public. Кажется простым, но вы теряете всю валидацию и инварианты, которые даёт инкапсуляция. По умолчанию — данные private с public-методами.
  • Забывать про значение по умолчанию у class. class Foo { int x; }; делает x приватным, поэтому foo.x = 5 не скомпилируется. Если вы имели в виду простой набор данных, используйте struct или добавьте метку public:.
  • Злоупотреблять protected. Это более слабая граница, чем private, и она имеет значение только при наследовании. Хвататься за неё повсюду — значит привязывать подклассы к полям, которые вы, возможно, захотите изменить.
  • Ожидать, что private — это средство безопасности. Это правило времени компиляции, предотвращающее случайный доступ, а не шифрование. Байты по-прежнему лежат в памяти; private — про чистый дизайн, а не про секретность.

Далее: Структуры

Теперь вы увидели, что struct — это на самом деле просто class, члены которого по умолчанию public. Следующая страница, структуры, подробно разбирает, когда это значение public-по-умолчанию — именно то, что вам нужно (лёгкие агрегаты для группировки связанных значений), и как struct используется в идиоматичном C++ наряду с полноценными классами.

Часто задаваемые вопросы

В чём разница между public, private и protected в C++?

Члены public доступны откуда угодно. Члены private доступны только изнутри того же класса (и его friend-ов). protected похож на private, но дополнительно позволяет производным классам обращаться к члену. По соглашению данные держат private, а поведение предоставляют через public-методы.

Являются ли члены приватными по умолчанию в C++?

В class — да: всё private, пока вы не напишете спецификатор доступа. В struct по умолчанию public. Это единственное значение по умолчанию и есть настоящая разница между class и struct в C++; у обоих могут быть методы, конструкторы и спецификаторы доступа.

Что делает ключевое слово friend в C++?

friend предоставляет одной конкретной функции или классу доступ к вашим членам private и protected. Это намеренное, узкое исключение из инкапсуляции — класс прямо называет, кому он доверяет, поэтому доступ никогда не предоставляется неявно.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ