访问说明符
在 member-specification 的 类/结构体 或 联合体 中,定义后续成员的可访问性。
在 base-specifier 的 派生类 声明中,定义后续基类的继承成员的可访问性。
目录 |
[编辑] 语法
public : member-declarations |
(1) | ||||||||
protected : member-declarations |
(2) | ||||||||
private : member-declarations |
(3) | ||||||||
public base-class | (4) | ||||||||
protected base-class | (5) | ||||||||
private base-class | (6) | ||||||||
无论公有、受保护还是私有继承,基类的私有成员始终对派生类不可访问。
[编辑] 解释
每个类成员(静态、非静态、函数、类型等)的名称都具有关联的“成员访问权限”。当在程序中的任何位置使用成员名称时,都会检查其访问权限,如果它不满足访问规则,则程序无法编译
#include <iostream> class Example { public: // all declarations after this point are public void add(int x) // member "add" has public access { n += x; // OK: private Example::n can be accessed from Example::add } private: // all declarations after this point are private int n = 0; // member "n" has private access }; int main() { Example e; e.add(1); // OK: public Example::add can be accessed from main // e.n = 7; // error: private Example::n cannot be accessed from main }
访问说明符使类的作者能够决定哪些类成员可供类的用户访问(即接口),哪些成员供类内部使用(即实现)。
[编辑] 详细信息
类的所有成员(成员函数的主体、成员对象的初始化器以及整个嵌套类定义)都可以访问该类可以访问的所有名称。成员函数内的局部类可以访问该成员函数可以访问的所有名称。
使用关键字 class
定义的类默认对其成员及其基类具有私有访问权限。使用关键字 struct
定义的类默认对其成员及其基类具有公有访问权限。联合体默认对其成员具有公有访问权限。
要向其他函数或类授予对受保护或私有成员的访问权限,可以使用友元声明。
可访问性适用于所有名称,与其来源无关,因此由 typedef 或 using 声明(继承构造函数除外)引入的名称会受到检查,而不是它引用的名称
class A : X { class B {}; // B is private in A public: typedef B BB; // BB is public }; void f() { A::B y; // error: A::B is private A::BB x; // OK: A::BB is public }
成员访问权限不影响可见性:私有和私有继承成员的名称是可见的,并且会被重载解析考虑,到不可访问基类的隐式转换仍然会被考虑,等等。成员访问检查是任何给定语言构造被解释后的最后一步。此规则的目的是用 public
替换任何 private
永远不会改变程序的行为。
在默认函数参数以及默认模板参数中使用的名称的访问检查在声明点执行,而不是在使用点执行。
虚函数的名称的访问规则在使用用于表示调用成员函数的对象的表达式类型时在调用点检查。最终覆盖者的访问权限被忽略
struct B { virtual int f(); // f is public in B }; class D : public B { private: int f(); // f is private in D }; void f() { D d; B& b = d; b.f(); // OK: B::f is public, D::f is invoked even though it's private d.f(); // error: D::f is private }
根据非限定名称查找为私有的名称,可以通过限定名称查找来访问
class A {}; class B : private A {}; class C : public B { A* p; // error: unqualified name lookup finds A as the private base of B ::A* q; // OK: qualified name lookup finds the namespace-level declaration };
可以通过继承图中的多个路径访问的名称具有访问权限最高的路径的访问权限
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 is accessible to C through B } };
任何数量的访问说明符都可以出现在类中,顺序不限。
成员访问说明符可能会影响类布局:非静态数据成员的地址仅保证对于未被访问说明符分隔的成员(直到 C++11)具有相同访问权限的成员(自 C++11 起),按声明顺序递增。 |
(直到 C++23) |
对于标准布局类型,所有非静态数据成员必须具有相同的访问权限。 |
(自 C++11 起) |
当在同一个类中重新声明成员时,它必须在相同的成员访问权限下进行
struct S { class A; // S::A is public private: class A {}; // error: cannot change access };
[编辑] 公有成员访问
公有成员构成类公共接口的一部分(公共接口的其他部分是通过ADL找到的非成员函数)。
类的公有成员在任何地方都可访问
class S { public: // n, E, A, B, C, U, f are public members int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f is accessible in main S s; s.n = S::B; // S::n and S::B are accessible in main S::U x; // S::U is accessible in main }
[编辑] 受保护成员访问
受保护成员构成类到其派生类的接口(这与类的公共接口不同)。
类的受保护成员仅可访问于
struct Base { protected: int i; private: void g(Base& b, struct Derived& d); }; struct Derived : Base { friend void h(Base& b, Derived& d); void f(Base& b, Derived& d) // member function of a derived class { ++d.i; // OK: the type of d is Derived ++i; // OK: the type of the implied '*this' is Derived // ++b.i; // error: can't access a protected member through // Base (otherwise it would be possible to change // other derived classes, like a hypothetical // Derived2, base implementation) } }; void Base::g(Base& b, Derived& d) // member function of Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Friend of Derived { ++d.i; // OK: friend of Derived can access a protected // member through an object of Derived // ++b.i; // error: friend of Derived is not a friend of Base } void x(Base& b, Derived& d) // non-member non-friend { // ++b.i; // error: no access from non-member // ++d.i; // error: no access from non-member }
当形成指向受保护成员的指针时,它必须在其声明中使用派生类
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // error: must name using Derived int Base::* ptr = &Derived::i; // OK } };
[编辑] 私有成员访问
私有成员构成类的实现,以及类其他成员的私有接口。
类的私有成员仅可访问于该类的成员和友元,无论这些成员是在相同还是不同的实例上
class S { private: int n; // S::n is private public: S() : n(10) {} // this->n is accessible in S::S S(const S& other) : n(other.n) {} // other.n is accessible in S::S };
显式转型(C 风格和函数风格)允许从派生左值转型到对其私有基类的引用,或从指向派生类的指针转型到指向其私有基类的指针。
[编辑] 继承
有关公有、受保护和私有继承的含义,请参见派生类。
[编辑] 关键字
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 1873 | C++98 | 受保护成员可供派生类的友元访问 | 设为不可访问 |