Модификаторы доступа в C++ public, private, protected
Модификаторы доступа в C++ определяют уровень видимости членов класса (полей и методов) для других частей программы. Они являются ключевым механизмом инкапсуляции в объектно-ориентированном программировании.
Основные модификаторы доступа
1. public (публичный доступ)
Члены, объявленные как public, доступны из любого места программы.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Example { public: int publicVar; void publicMethod() { cout << "Public method" << endl; } }; int main() { Example obj; obj.publicVar = 10; // Доступ разрешен obj.publicMethod(); // Доступ разрешен return 0; } |
2. private (приватный доступ)
Члены, объявленные как private, доступны только внутри самого класса и для друзей (friends) класса.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Example { private: int privateVar; void privateMethod() { cout << "Private method" << endl; } public: void accessPrivate() { privateVar = 20; // Доступ разрешен (внутри класса) privateMethod(); // Доступ разрешен (внутри класса) } }; int main() { Example obj; obj.privateVar = 30; // Ошибка доступ запрещен obj.privateMethod(); // Ошибка доступ запрещен obj.accessPrivate(); // Доступ к приватным членам через публичный метод return 0; } |
3. protected (защищенный доступ)
Члены, объявленные как protected, доступны внутри класса и для производных классов (при наследовании).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Base { protected: int protectedVar; void protectedMethod() { cout << "Protected method" << endl; } }; class Derived public Base { public: void accessProtected() { protectedVar = 40; // Доступ разрешен (в производном классе) protectedMethod(); // Доступ разрешен (в производном классе) } }; int main() { Derived obj; obj.protectedVar = 50; // Ошибка доступ запрещен obj.accessProtected(); // Доступ к защищенным членам через публичный метод return 0; } |
Модификаторы доступа при наследовании
При наследовании можно указать модификатор доступа, который определяет, как будут доступны унаследованные члены базового класса в производном классе.
1. public наследование
- public члены базового класса остаются public в производном
- protected члены остаются protected
- private члены остаются недоступными
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Base { public: int publicBaseVar; protected: int protectedBaseVar; private: int privateBaseVar; }; class Derived public Base { // publicBaseVar - public // protectedBaseVar - protected // privateBaseVar - недоступен }; int main() { Derived obj; obj.publicBaseVar = 1; // OK obj.protectedBaseVar = 2; // Ошибка obj.privateBaseVar = 3; // Ошибка return 0; } |
2. protected наследование
- public и protected члены базового класса становятся protected в производном
- private члены остаются недоступными
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Base { public: int publicBaseVar; protected: int protectedBaseVar; private: int privateBaseVar; }; class Derived protected Base { // publicBaseVar - protected // protectedBaseVar - protected // privateBaseVar - недоступен }; int main() { Derived obj; obj.publicBaseVar = 1; // Ошибка (теперь protected) return 0; } |
3. private наследование
- Все члены базового класса (public и protected) становятся private в производном
- private члены остаются недоступными
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Base { public: int publicBaseVar; protected: int protectedBaseVar; private: int privateBaseVar; }; class Derived private Base { // publicBaseVar - private // protectedBaseVar - private // privateBaseVar - недоступен }; int main() { Derived obj; obj.publicBaseVar = 1; // Ошибка (теперь private) return 0; } |
Пример с наследованием и модификаторами доступа
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include iostream using namespace std; class Animal { private: int privateAge; // Только внутри Animal protected: string protectedName; // Доступно в Animal и производных классах public: string publicSpecies; // Доступно везде Animal(string name, string species): protectedName(name), publicSpecies(species), privateAge(0) {} void printPrivate() { cout Private age privateAge endl; } }; class Dog public Animal { private: string breed; public: Dog(string name, string breed) Animal(name, Canine), breed(breed) {} void printInfo() { cout << "Name: " << protectedName << endl; // Доступ к protected cout << "Species: " << publicSpecies << endl; // Доступ к public cout << privateAge; // Ошибка - private недоступен printPrivate(); // Доступ через public метод базового класса cout << "Breed: " << breed << endl; } }; int main() { Dog myDog(Buddy, Golden Retriever); myDog.printInfo(); // Доступ к public членам cout << Species from main: " << myDog.publicSpecies << endl; // Ошибки доступа cout << myDog.protectedName; // protected - недоступен cout << myDog.privateAge; // private - недоступен cout << myDog.breed; // private - недоступен return 0; } |
Важные замечания
-
По умолчанию (без указания модификатора) в классах (class) все члены private, в структурах (struct) — public.
-
Модификаторы доступа можно использовать несколько раз в одном классе
|
1 2 3 4 5 6 7 8 9 |
class Example { public: int a; private: int b; public: int c; Снова public }; |
-
Дружественные функции (friend) могут получать доступ к private и protected членам.
-
Модификаторы доступа работают во время компиляции, а не выполнения программы.
Правильное использование модификаторов доступа — важная часть проектирования классов в C++, так как это помогает обеспечить инкапсуляцию и защитить внутреннее состояние объектов от неконтролируемого изменения.
Влияние модификаторов доступа на использование объектов производного класса
Модификаторы доступа при наследовании существенно влияют на то, как можно использовать объекты производного класса. Рассмотрим это подробно.
1. Public наследование (наиболее распространенный случай)
При public наследовании:
- Публичные члены базового класса остаются публичными в производном классе
- Защищенные члены остаются защищенными
- Приватные члены остаются недоступными
Пример:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class Base { public: int publicVar; void publicMethod() { cout << "Base public methodn"; } protected: int protectedVar; void protectedMethod() { cout << "Base protected methodn"; } private: int privateVar; void privateMethod() { cout << "Base private methodn"; } }; class Derived : public Base { public: void testAccess() { publicVar = 1; // OK - public publicMethod(); // OK - public protectedVar = 2; // OK - protected (доступ внутри производного класса) protectedMethod(); // OK - protected // privateVar = 3; // Ошибка - private недоступен // privateMethod(); // Ошибка - private недоступен } }; int main() { Derived d; d.publicVar = 10; // OK - осталось public d.publicMethod(); // OK - осталось public // d.protectedVar = 20; // Ошибка - protected недоступен извне // d.protectedMethod(); // Ошибка - protected недоступен извне d.testAccess(); // Доступ к protected через public метод return 0; } |
2. Protected наследование
При protected наследовании:
- Публичные и защищенные члены базового класса становятся защищенными в производном
- Приватные члены остаются недоступными
Последствия:
- Публичные члены базового класса больше не доступны через объект производного класса
- Они доступны только внутри производного класса и его наследников
Пример:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class Base { public: int publicVar; protected: int protectedVar; private: int privateVar; }; class Derived : protected Base { public: void testAccess() { publicVar = 1; // OK - теперь protected protectedVar = 2; // OK - protected // privateVar = 3; // Ошибка - private недоступен } }; class SecondDerived : public Derived { public: void testAgain() { publicVar = 10; // OK - унаследовано как protected protectedVar = 20; // OK - protected } }; int main() { Derived d; // d.publicVar = 100; // Ошибка - теперь protected d.testAccess(); // Доступ через public метод SecondDerived sd; // sd.publicVar = 200; // Ошибка - всё ещё protected sd.testAgain(); return 0; } |
3. Private наследование
При private наследовании:
- Все члены базового класса (public и protected) становятся private в производном
- Приватные члены остаются недоступными
Последствия:
- Все унаследованные члены доступны только внутри производного класса
- Недоступны через объект производного класса
- Недоступны в классах, наследующих от производного
Пример:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class Base { public: int publicVar; void publicMethod() { cout << "Base public methodn"; } protected: int protectedVar; void protectedMethod() { cout << "Base protected methodn"; } private: int privateVar; }; class Derived : private Base { public: void testAccess() { publicVar = 1; // OK - теперь private publicMethod(); // OK - теперь private protectedVar = 2; // OK - теперь private protectedMethod(); // OK - теперь private // privateVar = 3; // Ошибка - private недоступен } }; class SecondDerived : public Derived { public: void testAgain() { // publicVar = 10; // Ошибка - private в Derived // protectedVar = 20; // Ошибка - private в Derived } }; int main() { Derived d; // d.publicVar = 100; // Ошибка - теперь private // d.publicMethod(); // Ошибка - теперь private d.testAccess(); // Доступ через public метод return 0; } |
Практические последствия
-
Public наследование используется для отношения «является» (is-a), когда производный класс действительно представляет собой разновидность базового класса.
-
Protected/private наследование используются реже и обычно реализуют отношение «реализовано посредством» (implemented-in-terms-of), когда производный класс использует функциональность базового, но не является его разновидностью.
-
При protected наследовании клиенты производного класса теряют доступ к публичным членам базового класса.
-
При private наследовании и клиенты производного класса, и его наследники теряют доступ к членам базового класса.
Важные особенности
- Приведение типов:
- При public наследовании: Derived → Base работает автоматически
- При protected/private наследовании: требуется явное приведение
|
1 2 3 4 5 6 7 8 9 10 |
class Base {}; class Derived : private Base {}; int main() { Derived d; // Base* b = &d; // Ошибка при private наследовании Base* b = (Base*)&d; // Явное приведение работает return 0; } |
- Переопределение видимости: Можно «поднять» видимость отдельных членов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Base { public: void usefulMethod() {} }; class Derived : private Base { public: using Base::usefulMethod; // Делаем public снова }; int main() { Derived d; d.usefulMethod(); // Теперь доступно return 0; } |
- Отношения между классами:
- Public наследование: является (is-a)
- Private наследование: реализовано посредством (implemented-in-terms-of)
- Protected наследование: промежуточный вариант, редко используется
Правильный выбор типа наследования помогает точно выразить семантику отношений между классами и контролировать доступ к их членам.