Член класса может быть
private; то есть его имя могут использовать только члены и друзья класса, в котором он объявлен.
protected; то есть его имя может использоваться только членами и друзьями класса, в котором он объявлен, классами, производными от этого класса, и их друзьями (см[class.protected]. раздел "Ресурсы" ).
public; то есть его имя можно использовать где угодно без ограничения доступа.
Член класса также может получить доступ ко всем именам, к которым у класса есть доступ. Локальный класс функции-члена может иметь доступ к тем же именам, что и сама функция-член.113
Члены класса , определенного с помощью ключевого слова class являются private по умолчанию. Члены класса определяются с ключевыми словами struct или union являются public по умолчанию. [ Example:
class X { int a; // X::a is private by default }; struct S { int a; // S::a is public by default };
— end example ]
Контроль доступа применяется единообразно ко всем именам, независимо от того, упоминаются ли имена из объявлений или выражений. [ Note: Контроль доступа применяется к именам, назначенным friend declarations и using-declarations. ] В случае перегруженных имен функций управление доступом применяется к функции, выбранной с помощью разрешения перегрузки. [ Поскольку управление доступом применяется к именам, если управление доступом применяется к имени typedef, учитывается только доступность самого имени typedef. Доступность объекта, на который ссылается typedef, не рассматривается. Например, — end note Note:
class A { class B { }; public: typedef B BB; }; void f() { A::BB x; // OK, typedef name A::BB is public A::B y; // access error, A::B is private }
— end note ]
Следует отметить, access что контролируются члены и базовые классы, а не их visibility. Имена членов по-прежнему видны, и неявные преобразования в базовые классы по-прежнему рассматриваются, когда эти члены и базовые классы недоступны. Интерпретация данной конструкции устанавливается без учета контроля доступа. Если установленная интерпретация использует недоступные имена членов или базовые классы, конструкция имеет неправильный формат.
Все элементы управления доступом в пункте[class.access] влияют на возможность доступа к имени члена класса из объявления конкретной сущности, включая части объявления, предшествующие имени объявляемой сущности, и, если сущность является классом, определения членов этой сущности. класс, появляющийся вне класса member-specification. [ Note: Этот доступ также применяется к неявным ссылкам на конструкторы, функции преобразования и деструкторы. ] — end note
[ Example:
class A {
typedef int I; // private member
I f();
friend I g(I);
static I x;
template<int> struct Q;
template<int> friend struct R;
protected:
struct B { };
};
A::I A::f() { return 0; }
A::I g(A::I p = A::x);
A::I g(A::I p) { return 0; }
A::I A::x = 0;
template<A::I> struct A::Q { };
template<A::I> struct R { };
struct D: A::B, A { };
Здесь все виды использования A::I хорошо сформированы , потому что A::f, A::xиA::Q являются членами класса A и g иR являются друзьями класса A. Это означает, например, что проверка доступа при первом использовании A::I должна быть отложена до тех пор, пока не будет определено, что это использование A::I является типом возвращаемого значения члена класса A. Точно так же использованиеA::B as a base-specifierправильно сформировано, посколькуD является производным отA, поэтому проверку base-specifiers необходимо отложить до base-specifier-listтех пор, пока не будет просмотрено все. ] — end example
Имена в adefault argument связаны в точке объявления, и доступ проверяется в этой точке, а не в любых точках использования аргумента по умолчанию. Проверка доступа для аргументов по умолчанию в шаблонах функций и в функциях-членах шаблонов классов выполняется, как описано в[temp.inst].
Доступ к именам в default template-argument([temp.param]) проверяется в контексте, в котором они появляются, а не в каких-либо точках использования значения по умолчанию template-argument. [ Example:
class B { };
template <class T> class C {
protected:
typedef T TT;
};
template <class U, class V = typename U::TT>
class D : public U { };
D <C<B> >* d; // access error, C::TT is protected
— end example ]
Таким образом, разрешения на доступ являются транзитивными и кумулятивными для вложенных и локальных классов.
Объявления участников могут быть помечены access-specifier:
access-specifier : member-specificationopt
An access-specifier определяет правила доступа для членов, следующих за ним до конца класса или до тех пор, пока access-specifier не встретится другой . [ Example:
class X { int a; // X::a is private by default: class used public: int b; // X::b is public int c; // X::c is public };
— end example ]
Допускается любое количество спецификаторов доступа и никакого определенного порядка не требуется. [ Example:
struct S { int a; // S::a is public by default: struct used protected: int b; // S::b is protected private: int c; // S::c is private public: int d; // S::d is public };
— end example ]
[ Note: Влияние контроля доступа на порядок распределения элементов данных описано в[class.mem]. ] — end note
Когда член повторно объявляется в определении его класса, доступ, указанный при его повторном объявлении, должен быть таким же, как и при его первоначальном объявлении. [ Example:
struct S { class A; enum E : int; private: class A { }; // error: cannot change access enum E: int { e0 }; // error: cannot change access };
— end example ]
[ Note: В производном классе поиск имени базового класса найдет имя внедренного класса вместо имени базового класса в области, в которой он был объявлен. Имя внедренного класса может быть менее доступным, чем имя базового класса в области, в которой он был объявлен. ] — end note
[ Example:
class A { }; class B : private A { }; class C : public B { A* p; // error: injected-class-name A is inaccessible ::A* q; // OK };
— end example ]
Если класс объявлен как принадлежащийbase class другому классу с использованием public спецификатора доступа, public члены базового класса доступны как public члены производного класса, а protected члены базового класса доступны как protected члены производного класса. Если класс объявлен базовый класс для другого класса , используя protected спецификатор доступа, то public и protected члены базового класса доступны в качестве protected членов производного класса. Если класс объявлен базовый класс для другого класса , используя private спецификатор доступа, то public и protected члены базового класса доступны в качестве private членов производного класса114.
При отсутствии access-specifier для базового класса public предполагается, когда производный класс определяется с помощью, class-key struct и private предполагается, когда класс определяется с помощью class-key class. [ Example:
class B { /* ... */ }; class D1 : private B { /* ... */ }; class D2 : public B { /* ... */ }; class D3 : B { /* ... */ }; // B private by default struct D4 : public B { /* ... */ }; struct D5 : private B { /* ... */ }; struct D6 : B { /* ... */ }; // B public by default class D7 : protected B { /* ... */ }; struct D8 : protected B { /* ... */ };
Вот B это общественная база D2, D4и D6, собственная база D1, D3и D5, и защищенная база D7 и D8. ] — end example
[ Note: Член частного базового класса может быть недоступен как унаследованное имя члена, но доступен напрямую. Из-за правил дляpointer conversions иexplicit castsпреобразование указателя на производный класс в указатель на недоступный базовый класс может быть плохо сформированным, если используется неявное преобразование, но правильно сформированным, если используется явное приведение. Например,
class B { public: int mi; // non-static member static int si; // static member }; class D : private B { }; class DD : public D { void f(); }; void DD::f() { mi = 3; // error: mi is private in D si = 3; // error: si is private in D ::B b; b.mi = 3; // OK (b.mi is different from this->mi) b.si = 3; // OK (b.si is different from this->si) ::B::si = 3; // OK ::B* bp1 = this; // error: B is a private base class ::B* bp2 = (::B*)this; // OK with cast bp2->mi = 3; // OK: access through a pointer to B. }
— end note ]
Базовый класс B из N находится accessible в R, если
изобретенный публичный член B мог бы быть публичным членом N, или
R происходит в члене или друге класса N, а изобретенный публичный член B мог бы быть частным или защищенным членом N, или
R происходит в члене или друге класса, P производного от N, и изобретенный открытый член B будет частным или защищенным членом P, или
существует такой класс S , который B является базовым классом, S доступным в, R и S базовым классом, N доступным в R.
[ Example:
class B { public: int m; }; class S: private B { friend class N; }; class N: private S { void f() { B* p = this; // OK because class S satisfies the fourth condition above: B is a base class of N // accessible in f() because B is an accessible base class of S and S is an accessible // base class of N. } };
— end example ]
Если базовый класс доступен, можно неявно преобразовать указатель на производный класс в указатель на этот базовый класс ([conv.ptr],[conv.mem]). [ Note: Отсюда следует, что члены и друзья класса X могут неявно преобразовывать X* в указатель на частный или защищенный непосредственный базовый класс X. ] Доступ к члену зависит от класса, в котором член назван. Этот класс именования - это класс, в котором имя члена было просмотрено и найдено. [ Этот класс может быть явным, например, когда используется, или неявным, например, когда используется оператор (включая случаи, когда добавляется неявный « »). Если оба операторы доступа члена класса и используется , чтобы назвать элемент (как в ), классе именования элемента является классом обозначать те из (то есть ). ] Член доступен в момент, когда назван в классе, если — end note Note: qualified-idclass member access this->qualified-idp->T::mnested-name-specifierqualified-idT — end note mRN
m как участник N является публичным, или
m как член N является частным и R встречается в члене или друге класса N, или
m поскольку член N защищен и R встречается в члене или друге класса N, или в члене класса, P производного от N, где m как член P является общедоступным, частным или защищенным, или
существует базовый класс B в N который доступен на R, и m доступен в R когда названный в классе B. [ Example:
class B;
class A {
private:
int i;
friend void f(B*);
};
class B : public A { };
void f(B* p) {
p->i = 1; // OK: B* can be implicitly converted to A*, and f has access to i in A
}
— end example ]
Если оператор доступа к члену класса, включая неявный «this->», используется для доступа к нестатическому члену данных или нестатической функции-члену, ссылка имеет неправильный формат, если левый операнд (рассматриваемый как указатель в.операторе « » case) нельзя неявно преобразовать в указатель на класс именования правого операнда. [ Note: Это требование является дополнением к требованию, чтобы член был доступен с указанным именем. ] — end note
Как указано ранее в разделе[class.access], частные члены базового класса остаются недоступными даже для производных классов, если только friend объявления в определении базового класса не используются для явного предоставления доступа.
Друг класса - это функция или класс, которому дано разрешение на использование частных и защищенных имен членов из класса. Класс указывает своих друзей, если таковые имеются, посредством объявления друзей. Такие объявления предоставляют друзьям особые права доступа, но они не делают назначенных друзей членами класса дружбы. [ Example: Следующий пример иллюстрирует различия между участниками и друзьями:
class X { int a; friend void friend_set(X*, int); public: void member_set(int); }; void friend_set(X* p, int i) { p->a = i; } void X::member_set(int i) { a = i; } void f() { X obj; friend_set(&obj,10); obj.member_set(10); }
— end example ]
Объявление класса другом подразумевает, что к именам закрытых и защищенных членов класса, предоставляющего дружбу, можно получить доступ в base-specifiers объявлениях членов и дружественного класса. [ Example:
class A { class B { }; friend class X; }; struct X : A::B { // OK: A::B accessible to friend A::B mx; // OK: A::B accessible to member of friend class Y { A::B my; // OK: A::B accessible to nested member of friend }; };
— end example ] [ Example:
class X { enum { a=100 }; friend class Y; }; class Y { int v[X::a]; // OK, Y is a friend of X }; class Z { int v[X::a]; // error: X::a is private };
— end example ]
Класс не должен определяться в объявлении друга. [ Example:
class A {
friend class B { }; // error: cannot define class in friend declaration
};
— end example ]
friend Заявление , что не объявляет функцию должно иметь одну из следующих форм:
friend elaborated-type-specifier ; friend simple-type-specifier ; friend typename-specifier ;
[ Декларация может быть в (п , ). ] Если спецификатор типа в объявлении обозначает тип класса (возможно, квалифицированный cv), этот класс объявляется как друг; в противном случае объявление игнорируется. [ Note: friend declarationtemplate-declaration[temp][temp.friend] — end note friend friend Example:
class C; typedef C Ct; class X1 { friend C; // OK: class C is a friend }; class X2 { friend Ct; // OK: class C is a friend friend D; // error: no type-name D in scope friend class D; // OK: elaborated-type-specifier declares new class }; template <typename T> class R { friend T; }; R<C> rc; // class C is a friend of R<C> R<int> Ri; // OK: "friend int;" is ignored
— end example ]
Функция, впервые объявленная в объявлении друга, имеет связь с пространством имен, членом которого она является ([basic.link]). В противном случае функция сохраняет свою предыдущую связь ([dcl.stc]).
Когда friend объявление относится к перегруженному имени или оператору, только функция, указанная типами параметров, становится другом. Функция-член класса X может быть другом класса Y. [ Example:
class Y { friend char* X::foo(int); friend X::X(char); // constructors can be friends friend X::~X(); // destructors can be friends };
— end example ]
Функция может быть определена в объявлении друга класса тогда и только тогда, когда класс является нелокальным классом ([class.local]), имя функции неквалифицировано и функция имеет область пространства имен. [ Example:
class M { friend void f() { } // definition of global f, a friend of M, // not the definition of a member function };
— end example ]
Такая функция неявно являетсяinline function. friend Функция , определенная в классе находится в (лексической) области видимости класса , в котором она определена. Дружественная функция, определенная вне класса, не ([basic.lookup.unqual]).
Имя, назначенное объявлением друга, должно быть доступно в области действия класса, содержащего объявление друга. Смысл объявления друга одинаков, независимо от того, присутствует ли объявление друга в части классаprivate, protected или public ([class.mem]) member-specification.
Дружба не является ни наследственной, ни переходной. [ Example:
class A { friend class B; int a; }; class B { friend class C; }; class C { void f(A* p) { p->a++; // error: C is not a friend of A despite being a friend of a friend } }; class D : public B { void f(A* p) { p->a++; // error: D is not a friend of A despite being derived from a friend } };
— end example ]
Если объявление друга появляется в a,local class а указанное имя является неквалифицированным именем, предыдущее объявление ищется без учета областей, которые находятся за пределами самой внутренней охватывающей неклассовой области. Для объявления дружественной функции, если нет предварительного объявления, программа имеет неправильный формат. Для объявления дружественного класса, если предшествующего объявления нет, указанный класс принадлежит к самой внутренней охватывающей неклассовой области, но если на него впоследствии будет сделана ссылка, его имя не будет найдено с помощью поиска по имени до тех пор, пока соответствующее объявление не будет предоставлено в самая внутренняя охватывающая неклассовая область видимости. [ Example:
class X; void a(); void f() { class Y; extern void b(); class A { friend class X; // OK, but X is a local class, not ::X friend class Y; // OK friend class Z; // OK, introduces local class Z friend void a(); // error, ::a is not considered friend void b(); // OK friend void c(); // error }; X* px; // OK, but ::X is found Z* pz; // error, no Z is found }
— end example ]
Дополнительная проверка доступа, помимо описанных ранее в пункте[class.access] , применяется, когда нестатический член данных или нестатическая функция-член является защищенным членом своего класса именования ([class.access.base]).115 Как описано ранее, доступ к защищенному члену предоставляется, потому что возникает ссылка у друга или члена какого-то классаC. Если доступ должен формировать указатель на member ([expr.unary.op]), то nested-name-specifierдолжен обозначатьC или класс, производный от C. Все другие обращения включают (возможно, неявный) object expression. В этом случае класс объектного выражения должен быть C классом, производным отC. [ Example:
class B { protected: int i; static int j; }; class D1 : public B { }; class D2 : public B { friend void fr(B*,D1*,D2*); void mem(B*,D1*); }; void fr(B* pb, D1* p1, D2* p2) { pb->i = 1; // ill-formed p1->i = 2; // ill-formed p2->i = 3; // OK (access through a D2) p2->B::i = 4; // OK (access through a D2, even though naming class is B) int B::* pmi_B = &B::i; // ill-formed int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*) B::j = 5; // ill-formed (not a friend of naming class B) D2::j = 6; // OK (because refers to static member) } void D2::mem(B* pb, D1* p1) { pb->i = 1; // ill-formed p1->i = 2; // ill-formed i = 3; // OK (access through this) B::i = 4; // OK (access through this, qualification ignored) int B::* pmi_B = &B::i; // ill-formed int B::* pmi_B2 = &D2::i; // OK j = 5; // OK (because j refers to static member) B::j = 6; // OK (because B::j refers to static member) } void g(B* pb, D1* p1, D2* p2) { pb->i = 1; // ill-formed p1->i = 2; // ill-formed p2->i = 3; // ill-formed }
— end example ]
Эта дополнительная проверка не применяется к другим членам, например, статическим элементам данных или константам элементов перечислителя.
Правила доступа (пункт[class.access]) для виртуальной функции определяются ее объявлением и не зависят от правил для функции, которая позже переопределяет ее. [ Example:
class B { public: virtual int f(); }; class D : public B { private: int f(); }; void f() { D d; B* pb = &d; D* pd = &d; pb->f(); // OK: B::f() is public, D::f() is invoked pd->f(); // error: D::f() is private }
— end example ]
Если имя может быть достигнуто несколькими путями через граф множественного наследования, доступ будет тем путем, который дает наибольший доступ. [ Example:
class W { public: void f(); };
class A : private virtual W { };
class B : public virtual W { };
class C : public A, public B {
void f() { W::f(); } // OK
};
Поскольку W::f() доступен C::f() по общедоступному пути B, доступ разрешен. ] — end example
Вложенный класс является членом и поэтому имеет те же права доступа, что и любой другой член. Члены включающего класса не имеют специального доступа к членам вложенного класса;access rules должны соблюдаться обычные правила. [ Example:
class E { int x; class B { }; class I { B b; // OK: E::I can access E::B int y; void f(E* p, int i) { p->x = i; // OK: E::I can access E::x } }; int g(I* p) { return p->y; // error: I::y is private } };
— end example ]