Управление доступом к членам (C++)
Элементы управления доступом позволяют отделять public
интерфейс класса от private
сведений о реализации и protected
членов, которые используются только производными классами. Спецификатор доступа действует для всех членов, объявленных после него, пока не будет объявлен следующий спецификатор доступа.
class Point
{
public:
Point( int, int ) // Declare public constructor.;
Point();// Declare public default constructor.
int &x( int ); // Declare public accessor.
int &y( int ); // Declare public accessor.
private: // Declare private state variables.
int _x;
int _y;
protected: // Declare protected function for derived classes only.
Point ToWindowCoords();
};
Доступ по умолчанию находится private
в классе и public
в структуре или союзе. Спецификаторы доступа класса могут использоваться любое количество раз и в любом порядке. Выделение хранилища для объектов типов классов зависит от реализации. Однако компиляторы должны гарантировать назначение элементов последовательно более высоким адресам памяти между описателями доступа.
Управление доступом к членам
Тип доступа | Значение |
---|---|
private |
Члены класса, объявленные как private могут использоваться только функциями-членами и друзьями (классами или функциями) класса. |
protected |
Члены класса, объявленные как protected могут использоваться функциями-членами и друзьями (классами или функциями) класса. Кроме того, они могут использоваться производными классами данного класса. |
public |
Члены класса, объявленные как public можно использовать любой функцией. |
Управление доступом помогает предотвратить использование объектов способами их использования. Эта защита теряется при выполнении явных преобразований типов (приведения).
Примечание.
Управление доступом одинаково применимо ко всем именам: функциям-членам, данным члена, вложенным классам и перечислителям.
Управление доступом в производном классе
Доступность в производном классе членов базового класса и унаследованных членов определяется двумя следующими факторами.
Объявляет ли производный класс базовым классом
public
с помощью описателя доступа.Какой доступ к членам предоставляется в базовом классе.
В следующей таблице показана взаимосвязь между этими факторами и определен доступ к членам в базовом классе.
Доступ к членам в базовом классе
private |
protected |
public |
---|---|---|
Всегда недоступно с любым доступом к производным данным | private в производном классе, если используется private производное |
private в производном классе, если используется private производное |
protected в производном классе, если используется protected производное |
protected в производном классе, если используется protected производное |
|
protected в производном классе, если используется public производное |
public в производном классе, если используется public производное |
В следующем примере показано наследование доступа:
// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
int PublicFunc(); // Declare a public member.
protected:
int ProtectedFunc(); // Declare a protected member.
private:
int PrivateFunc(); // Declare a private member.
};
// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
void foo()
{
PublicFunc();
ProtectedFunc();
PrivateFunc(); // function is inaccessible
}
};
class DerivedClass2 : private BaseClass
{
void foo()
{
PublicFunc();
ProtectedFunc();
PrivateFunc(); // function is inaccessible
}
};
int main()
{
DerivedClass1 derived_class1;
DerivedClass2 derived_class2;
derived_class1.PublicFunc();
derived_class2.PublicFunc(); // function is inaccessible
}
В DerivedClass1
, функция-член PublicFunc
является public
членом и ProtectedFunc
является protected
членом, так как BaseClass
является базовым классом public
. PrivateFunc
BaseClass
имеет значение private
, и недоступно для всех производных классов.
В DerivedClass2
функциях PublicFunc
и ProtectedFunc
считаются private
членами, так как BaseClass
это базовый private
класс. Опять же, это значит, PrivateFunc
что private
BaseClass
он недоступен для любых производных классов.
Производный класс можно объявить без спецификатора доступа базового класса. В таком случае производный класс считается, private
если объявление производного класса использует class
ключевое слово. Производные считаются, public
если объявление производного класса использует struct
ключевое слово. Например, приведенный ниже код
class Derived : Base
...
эквивалентно правилу
class Derived : private Base
...
Аналогично код
struct Derived : Base
...
эквивалентно правилу
struct Derived : public Base
...
Члены, объявленные как private
имеющие доступ, недоступны для функций или производных классов, если эти функции или классы не объявлены с помощью friend
объявления в базовом классе.
Тип union
не может иметь базовый класс.
Примечание.
При указании private базового класса рекомендуется явно использовать private
ключевое слово чтобы пользователи производного класса понимали доступ к члену.
Управление доступом и статические члены
При указании базового класса как private
он влияет только на нестатические члены. Открытые статические члены по-прежнему доступны в производных классах. Однако доступ к членам базового класса с помощью указателей, ссылок или объектов может потребовать преобразования, который снова применяет управление доступом. Рассмотрим следующий пример:
// access_control.cpp
class Base
{
public:
int Print(); // Nonstatic member.
static int CountOf(); // Static member.
};
// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};
// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
int ShowCount(); // Nonstatic member.
};
// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
// Call static member function CountOf explicitly.
int cCount = ::Base::CountOf(); // OK.
// Call static member function CountOf using pointer.
cCount = this->CountOf(); // C2247: 'Base::CountOf'
// not accessible because
// 'Derived1' uses 'private'
// to inherit from 'Base'
return cCount;
}
В предыдущем примере кода управление доступом запрещает преобразование указателя на Derived2
к указателю на Base
. Указатель this
неявно имеет тип Derived2 *
. Чтобы выбрать функцию CountOf
, this
необходимо преобразовать в тип Base *
. Такое преобразование не допускается, так как Base
является косвенным базовым классомDerived2
private. Преобразование в private тип базового класса допустимо только для указателей на непосредственные производные классы. Поэтому указатели типа Derived1 *
можно преобразовать в тип Base *
.
Явный вызов CountOf
функции без использования указателя, ссылки или объекта для его выбора не подразумевает преобразования. Именно поэтому вызов разрешен.
Члены и друзья производного класса T
, могут преобразовать указатель в указатель T
на прямой базовый private класс T
.
Доступ к виртуальным функциям
Управление доступом, применяемое к virtual
функциям, определяется типом, используемым для вызова функции. Переопределение объявлений функции не влияет на управление доступом для данного типа. Например:
// access_to_virtual_functions.cpp
class VFuncBase
{
public:
virtual int GetState() { return _state; }
protected:
int _state;
};
class VFuncDerived : public VFuncBase
{
private:
int GetState() { return _state; }
};
int main()
{
VFuncDerived vfd; // Object of derived type.
VFuncBase *pvfb = &vfd; // Pointer to base type.
VFuncDerived *pvfd = &vfd; // Pointer to derived type.
int State;
State = pvfb->GetState(); // GetState is public.
State = pvfd->GetState(); // C2248 error expected; GetState is private;
}
В предыдущем примере вызов виртуальной функции GetState
с помощью указателя на вызовы VFuncDerived::GetState
типов VFuncBase
и GetState
рассматривается как public
. Однако вызов GetState
с помощью указателя типа VFuncDerived
является нарушением управления доступом, так как GetState
объявляется private
в классе VFuncDerived
.
Внимание
Виртуальную функцию GetState
можно вызвать с помощью указателя на базовый класс VFuncBase
. Это не означает, что вызываемая функция является базовой версией этой функции.
Управление доступом с множественным наследованием
В решетках множественного наследования, в которых используются виртуальные базовые классы, к конкретным именам можно обращаться несколькими путями. Поскольку в разных путях могут применяться разные средства управления доступом, компилятор выбирает тот путь, который позволяет получить наиболее широкий доступ. См. следующий рисунок:
На схеме показана следующая иерархия наследования: класс VBase является базовым классом. Класс LeftPath наследует от VBase с помощью виртуальной виртуальной private базы данных. Класс RightPath также наследует от VBase, но с помощью виртуальной виртуальной public базы данных. Наконец, класс Производный наследует от класса LeftPath и класса RightPath с помощью public LeftPath, public RightPath.
Доступ вдоль линий графа наследования
На этом рисунке обращение к имени, которое было объявлено в классе VBase
, всегда будет выполняться через класс RightPath
. Правильный путь является более доступным, так как RightPath
объявляется VBase
как базовый public
класс, а LeftPath
объявляется VBase
как private
.
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по