访问说明符
在 类/结构体 或 联合体 的 成员-规范 中,定义后续成员的可访问性。
在 派生类 声明的 基类-规范 中,定义后续基类的继承成员的可访问性。
目录 |
[编辑] 语法
public : 成员-声明 |
(1) | ||||||||
protected : 成员-声明 |
(2) | ||||||||
private : 成员-声明 |
(3) | ||||||||
public 基类 | (4) | ||||||||
protected 基类 | (5) | ||||||||
private 基类 | (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 }
成员访问不影响可见性:私有和私有继承成员的名称是可见的,并被重载决议考虑,对不可访问基类的隐式转换仍然被考虑,等等。成员访问检查是在解释任何给定语言结构之后的最后一步。此规则的目的是将任何 private
替换为 public
绝不会改变程序的行为。
对 默认函数实参 中以及默认 模板形参 中使用的名称的访问检查是在声明点执行的,而不是在使用点执行的。
虚函数 名称的访问规则在调用点使用用于表示调用成员函数的对象的表达式类型进行检查。最终重载器的访问权限被忽略。
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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 1873 | C++98 | 保护成员可被派生类的友元访问 | 变为不可访问 |