成员访问的三种权限

面试官: 在 C++ 的类(class)定义中,我们有 public, protected, 和 private 这三个关键字来控制成员的访问权限。

你能简单解释一下,这三种权限分别意味着什么吗?也就是说,什么样的代码可以访问被它们修饰的成员?

回答:

  • public 修饰的成员,可以被该类的对象、该类的子类成员,以及该类的其他成员访问。(最宽松)
  • private 修饰的成员,只能被该类的其他成员访问,而不能被对象和子类成员访问。(最严格)
  • protected 修饰的成员,可以被该类的其他成员和子类的成员访问,但不能被该类的对象访问。(介于 publicprivate 之间)

Gemini 更好的概括:

  • public: 类内、子类、类外(通过对象)都能访问。权限最开放。
  • private: 仅限类内访问。权限最严格,实现了完全的封装。
  • protected: 类内、子类可以访问,但类外不行。权限介于两者之间,专门为继承体系设计。

类继承的三种权限

面试官 (深入提问): 好的,你对成员访问权限很清楚。现在我们把这个概念和继承结合起来。

C++ 中,继承也有三种方式:public 继承、protected 继承和 private 继承。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Base {
public:
    int public_member;
protected:
    int protected_member;
private:
    int private_member;
};

class Derived_Public : public Base { ... };
class Derived_Protected : protected Base { ... };
class Derived_Private : private Base { ... };

请问,这三种不同的继承方式,会如何影响基类 (Base) 的成员派生类 (Derived) 中的访问权限?

换句话说:

  1. Derived_Public 内部,public_memberprotected_member 分别是什么权限?
  2. Derived_Protected 内部呢?
  3. Derived_Private 内部呢?
  4. 基类的 private_member 在任何情况下,派生类能访问到吗?

回答:

首先,private 成员是基类私有的,派生类无法触及。因此,任何情况下,派生类都访问不到。

其次,关于其他成员的继承,我们有如下黄金法则:

基类成员在派生类中的最终访问权限,取决于两者中更严格(权限更小)的那个

这里说的“两者”,指的就是基类成员本身的访问权限,和派生类继承基类时的权限。

用下面的表格很好理解:

基类成员权限public 继承后protected 继承后private 继承后
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private(不可访问)(不可访问)(不可访问)

其中 public 继承是我们平时用得最多的,它的行为也最符合直觉。protectedprivate 继承在实际工程中用得相对较少,但理解它们有助于我们完整地掌握 C++ 的权限体系。

因此,上面问题的答案是:

  1. Derived_Public 内部,public_memberpublic 权限,protected_memberprotected 权限,与基类中一致。
  2. Derived_Protected 内部,public_memberprotected_member 均为 protected 权限。
  3. Derived_Private 内部,public_memberprotected_member 均为 private 权限。
  4. 在任何情况下,基类的 private_member 都无法被派生类访问。

structclass 之间的辨析

面试官 (最终问题): 很好,你对成员权限和继承权限都有了清晰的理解。现在我们来聊一个 C++ 中特有的概念:structclass

在 C++ 中,structclass 都可以用来定义一个类,它们都可以有成员变量、成员函数、构造函数、析构函数,也都可以进行继承。

从功能上看,它们几乎是一样的。那么,请问 structclass 在 C++ 中,它们唯一的区别是什么?

另外,从编程风格和设计意图上讲,我们通常在什么情况下倾向于使用 struct,又在什么情况下倾向于使用 class

回答:

  • 我认为,它们之间唯一的区别是:如果没有明文规定,那么 struct 所有的成员权限默认为 public,而 class 的所有成员权限默认为 private
  • 从编程风格和设计意图上来说,
    • 如果需要设计一个功能简单、关系较少的类,我们倾向于使用 struct
    • 如果需要设计一个功能复杂、关系很多,且需要设计对外接口的类,我们倾向于使用 class

面试官 (总结与肯定): 你说的完全正确。

1. 唯一的语法区别:默认访问权限

  • struct: 成员的默认访问权限public
  • class: 成员的默认访问权限private

这也是它们与 C 语言中 struct 行为保持某种兼容性的体现(C 的 struct 成员都是公有的)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct MyStruct {
    int x; // 默认是 public
};

class MyClass {
    int x; // 默认是 private
};

int main() {
    MyStruct s;
    s.x = 10; // OK

    MyClass c;
    // c.x = 10; // Error! 'x' is private
}

2. 编程风格和设计意图的区别

你对这一点的理解,正是一名优秀 C++ 程序员应有的素养。这已经超出了“语法”的范畴,进入了“代码设计哲学”的层面。

  • 使用 struct 的场景:

    • 当我们想定义一个纯粹的数据集合 (Plain Old Data, POD) 时。
    • 这个数据结构的主要职责就是聚合数据,而没有什么复杂的行为(成员函数)或不变量(需要 private 保护的内部状态)。
    • 比如,一个 Point 结构体,包含 xy 坐标;一个 RGBColor 结构体,包含 r, g, b 三个分量。这些成员通常都是 public 的,因为它们就是这个结构体存在的全部意义。
  • 使用 class 的场景:

    • 当我们想定义一个拥有复杂行为内部状态的对象时。
    • 这个对象需要通过 public 接口来对外提供服务,同时需要用 privateprotected隐藏(封装) 其内部的实现细节和数据,以保证其不变量 (invariants) 不被破坏。
    • 比如,一个 FileDownloader 类,它内部可能有 privatesocket_buffer_ 等成员,对外只提供 publicdownload()getProgress() 等接口。

总结来说,选择 struct 还是 class,更多的是向其他阅读代码的人传递一种设计意图的信号:我这里定义的是一个简单的数据包 (struct),还是一个具有封装性的、有复杂行为的对象 (class)。