Menu

C++ 访问修饰符:public、private、protected

C++ 中 public、private 和 protected 如何控制谁能访问类的成员:这是封装的基础,还包括 getter、setter 和 friend 这个例外通道。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

谁能访问你的数据

写第一个类时,你很可能把所有成员都暴露给了外部。这样能用,但它丢掉了类存在的一个主要理由:封装,即隐藏类的内部状态,让程序的其余部分只能通过受控的接口与它交互。访问修饰符就是用来划定这条边界的。

访问修饰符正好有三个:public、private 和 protected。每一个都会标记它后面的成员,而这个标记决定了哪些代码可以读写这些成员。用对了,你的类会自己守住规则;用错了,程序中任何地方的 bug 都可能破坏对象的状态。

三种访问修饰符

访问修饰符是一个关键字加一个冒号。在它之后、下一个访问修饰符之前声明的所有成员,都属于这个访问级别。

取消最后一行的注释,编译器就会拒绝编译:balance 是 private,所以 main 不能直接改动它。这正是目的所在:修改余额的唯一途径是 deposit,这意味着以后你可以在一个地方加入校验(不允许负数存款、记录日志、设置上限),并且确信它总会被执行。

完整的对照如下:

//             reachable from...
// public      anywhere (any code that has the object)
// private     only the same class's own members (+ friends)
// protected   the same class's members AND derived classes (+ friends)

class 与 struct:默认访问级别

访问修饰符块想写多少就写多少,顺序也随意。而在你写下第一个访问修饰符之前,成员的访问级别取决于你用的是 class 还是 struct:

  • 在 class 中,成员默认是 private。
  • 在 struct 中,成员默认是 public。

这个默认值是这两个关键字在语言层面上唯一的区别。struct 也可以像 class 一样拥有方法、构造函数和 private 部分。

惯例是:单纯打包公开数据时用 struct,需要行为和隐藏状态时用 class。但编译器并不强制这一点,区别只在于默认值。

用 getter 和 setter 实现封装

日常的写法是:数据放在 private 部分,由 public 方法提供受控的访问。只读的 getter 返回值;setter 在赋值之前先做校验。这正是 private 发挥作用的地方。

因为 celsius 是 private,所以没有办法偷偷塞进一个非法值:每一次写入都必须经过 setCelsius,由它守住不变量。注意 getter 都标记了 const:它们承诺不修改对象,所以对 const Temperature 对象也能调用。

protected 与继承

只有涉及继承时,protected 才有意义。对外部代码来说,它和 private 一样,但派生类可以访问它。把它用于子类确实需要、但仍不应对外公开的成员。

新手的一个常见错误是“以防子类需要”,给每个数据成员都加上 protected。这会悄悄扩大类的契约:现在每个子类都可能依赖这个字段,你就不能随意修改它了。优先使用 private,只有当派生类确实需要访问时才改为 protected。

friend 这个例外通道

有时某个外部函数或类确实需要看到你的内部,典型的例子是重载 << 运算符,它不能写成成员函数。friend 关键字允许这个指定的实体访问你的 private 和 protected 成员,除此之外谁都不行。

friend 是一种有意为之、精准的例外:由类自己明确指定它信任谁,所以没有人能从外部给自己授予访问权限。要谨慎使用;如果你发现自己加了很多 friend,可能这些成员一开始就不该是 private,或者你的设计需要重新考虑。

要避免的常见错误

  • 把所有成员都设为 public。 看起来省事,但你会失去封装带来的所有校验和不变量。默认把数据设为 private,通过 public 方法访问。
  • 忘了 class 的默认值。 class Foo { int x; }; 中的 x 是私有的,所以 foo.x = 5 无法编译。如果你想要的是单纯的数据集合,就用 struct,或者加上 public: 标记。
  • 滥用 protected。 它的边界比 private 弱,而且只在继承时才有意义。到处使用它,会让子类和你以后可能想修改的字段耦合在一起。
  • 以为 private 是一种安全机制。 它是一条防止意外访问的编译期规则,而不是加密。字节仍然在内存里;private 关乎清晰的设计,而不是保密。

下一篇:结构体

你已经看到,struct 其实就是成员默认为 public 的 class。下一篇结构体会深入讲解什么时候这种默认公开正是你想要的,也就是把相关的值组合在一起的轻量聚合体,以及在地道的 C++ 中如何把 struct 和功能完整的类搭配使用。

常见问题

C++ 中 public、private 和 protected 有什么区别?

public 成员在任何地方都能访问。private 成员只能在同一个类的内部(以及它的 friend)访问。protected 和 private 类似,但还允许派生类访问该成员。按照惯例,数据设为 private,行为则通过 public 方法对外提供。

C++ 的成员默认是 private 吗?

在 class 中是的,在写下访问修饰符之前,所有成员都是 private。在 struct 中,默认是 public。这个默认值是 C++ 中 class 和 struct 唯一真正的区别;两者都可以有方法、构造函数和访问修饰符。

C++ 的 friend 关键字有什么作用?

friend 允许某个特定的函数或类访问你的 private 和 protected 成员。这是对封装有意为之的、范围很窄的例外:由类自己明确指定它信任谁,所以访问权限永远不会被隐式授予。

Coddy 编程语言插图

和 Coddy 一起学编程

开始