C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
访问说明符
在 class/struct 或 union 的 成员说明 中,定义其后继成员的可访问性。
在派生类声明的 基类说明符 中,定义继承自其后继的基类的成员的可访问性。
语法
public : 成员说明
|
(1) | ||||||||
protected : 成员说明
|
(2) | ||||||||
private : 成员说明
|
(3) | ||||||||
public 基类 | (4) | ||||||||
protected 基类 | (5) | ||||||||
private 基类 | (6) | ||||||||
解释
每个类成员(静态、非静态、函数、类型等)的名字都具有与其关联的“成员访问”。在程序的任何位置使用成员的名字时,检查其访问,而且若它不满足访问规则,则程序不能编译:
#include <iostream> class Example { public: // 此点后的所有声明为公开 void add(int x) { // 成员 "add" 具有公开访问 n += x; // OK:从 Example::add 可以访问受保护的 Example::n } private: // 此点后的所有声明为私有 int n = 0; // 成员 "n" 具有私有访问 }; int main() { Example e; e.add(1); // OK:从 main 可以访问公开的 Example::add // e.n = 7; // 错误:从 main 不能访问私有的 Example::n }
访问说明符给予类作者决定哪些类成员能被类的用户所访问(即类的接口),而哪些成员用于内部使用(即其实现)的能力。
细节
类的所有成员(成员函数体,成员对象的初始化器,以及整个嵌套类定义),都拥有对类所能访问的所有名字的访问权。成员函数内的局部类拥有对成员函数所能访问的所有名字的访问权。
以关键词 class
定义的类,其成员和其基类默认具有私有访问。以关键词 struct
定义的类,其成员和其基类默认具有公开访问。union 的成员默认具有公开访问。
若要向其他函数或类授予对受保护或私有成员的访问权,可使用友元声明。
可访问性应用到所有名字,而不考虑其来源,因此受检查的是以 typedef 或 using 声明引入的名字,而非其所指涉的名字:
class A : X { class B { }; // B 在 A 中为私有 public: typedef B BB; // BB 为公开 }; void f() { A::B y; // 错误:A::B 为私有 A::BB x; // OK:A::BB 为公开 }
成员访问不影响可见性:私有和私有继承的成员对重载决议可见并被其考虑,到不可访问基类的隐式转换也仍被考虑,等等。成员访问检查是对任何给定语言构造进行解释之后的最后一步。此规则的目的是使得以 public
替换任何 private
时始终不会改变程序的行为。
对于默认函数实参中以及默认模板形参中所使用的名字的访问检查,在声明点而非在使用点进行。
对虚函数的名字的访问规则,在调用点,使用(用于代表调用该成员函数的对象的)表达式的类型进行检查。忽略最终覆盖函数的访问:
struct B { virtual int f(); }; // f 在 B 中为公开 class D : public B { private: int f(); }; // f 在 D 中为私有 void f() { D d; B& b = d; b.f(); // OK:B::f() 为公开,调用 D::f(),即使它为私有 d.f(); // 错误:D::f() 为私有 }
根据无限定名字查找为私有的名字,有可能通过有限定名字查找访问:
class A {}; class B : private A { }; class C : public B { A* p; // 错误:无限定名字查找找到作为 B 的私有基类的 A ::A* q; // OK:有限定名字查找找到命名空间层级的声明 };
可通过继承图中多条路径访问的名字,具有带最大访问的路径上的访问:
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 通过 B 对 C 是可访问的 };
类中可以任何顺序出现任何数量的访问说明符。成员访问说明符可能影响类的布局:非静态数据成员的地址只保证对于具有相同访问的成员以声明顺序增加。对于标准布局类型 (StandardLayoutType) ,所有非静态数据成员必须具有相同访问。
在类中重声明成员时,必须在相同成员访问下进行:
struct S { class A; // S::A 公开 private: class A {}; // 错误:不能更改访问 };
公开成员访问
公开成员组成类公开接口的一部分(公开接口的其他部分是由 ADL 所找到的非成员函数)。
类的公开成员可在任何位置访问。
class S { public: // n、E、A、B、C、U、f 是公开成员 int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f 于 main 可访问 S s; s.n = S::B; // S::n 与 S::B 于 main 可访问 S::U x; // S::U 于 main 可访问 }
受保护成员访问
受保护成员访问组成针对类对其派生类的接口(与类的公开接口有别)。
类的受保护成员只能为下列者所访问
struct Base { protected: int i; private: void g(Base& b, struct Derived& d); }; struct Derived : Base { void f(Base& b, Derived& d) // 派生类的成员函数 { ++d.i; // OK:d 的类型是 Derived ++i; // OK:隐含的 '*this' 的类型为 Derived // ++b.i; // 错误:不能通过 Base 访问受保护成员 // (否则可能更改另一派生类,假设为 Derived2 的基实现) } }; void Base::g(Base& b, Derived& d) // Base 的成员函数 { ++i; // OK ++b.i; // OK ++d.i; // OK } void x(Base& b, Derived& d) // 非成员非友元 { // ++b.i; // 错误:非成员不能访问 // ++d.i; // 错误:非成员不能访问 }
组成指向受保护成员的指针时,必须在其声明中使用派生类:
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // 错误:必须使用 Derived 来指名 int Base::* ptr = &Derived::i; // OK } };
私有成员访问
私有成员组成类的实现,以及针对类的其他成员的私有接口。
类的私有成员仅对类的成员和友元可访问,无关乎成员在相同还是不同实例:
class S { private: int n; // S::n 私有 public: S() : n(10) {} // this->n 可于 S::S 访问 S(const S& other) : n(other.n) {} // other.n 可于 S::S 访问 };
显式转型(C 风格与函数风格)允许从派生类左值转型为到其私有基类的引用,或从指向派生类的指针转型为指向其私有基类的指针。
继承
公开、受保护和私有继承的含义,见派生类。