类成员的访问控制

一、访问控制的基本思想

类的成员包含数据成员和函数成员,分别描述问题的属性和行为。以钟表为例,不论是何种钟表,都记录着时间值,都有显示面板、旋钮或按钮。正常用户只需要通过面板查看时间,通过旋钮调节时间。面板、按钮或旋钮就是接触和使用钟表的仅有途径,它们对应类中的公有接口。

而钟表内部记录的时间值(时、分、秒),使用者无法直接触碰,只能通过外部接口间接访问,它们对应类中的私有成员。

这种”外部只能走接口,内部数据被隐藏”的机制,就是访问控制。

二、三种访问属性

访问属性关键字类内部类外部(通过对象)派生类中作用
公有类型public可访问可访问可访问定义类的外部接口
私有类型private可访问不可访问不可访问隐藏内部数据和实现细节
保护类型protected可访问不可访问可访问继承体系中向派生类暴露成员

三、公有成员——类的外部接口

公有类型成员定义了类的外部接口。外界只能通过 public 成员与类的对象进行交互。

以时钟类为例,ShowTime() 和 SetTime() 是公有成员函数,使用者通过它们查看和设置时间:

Clock c;
c.SetTime(9, 30, 0);   // 合法:SetTime 是 public
c.ShowTime();          // 合法:ShowTime 是 publicCode language: JavaScript (javascript)

公有成员是类与外界通信的唯一通道。设计类时,应将所有需要对外提供的功能声明为 public。

四、私有成员——隐藏内部实现

私有成员只能被本类的成员函数访问,来自类外部的任何访问都是非法的。私有成员将内部数据结构完全隐藏在类中,保护数据的安全性。

时钟类中的 hour、minute、second 都是私有成员:

class Clock
{
private:
    int hour, minute, second;    // 私有成员

public:
    void SetTime(int h, int m, int s)
    {
        hour = h;               // 合法:类内成员函数可访问 private 成员
        minute = m;
        second = s;
    }

    void ShowTime()
    {
        cout << hour << ":" << minute << ":" << second << endl;  // 合法
    }
};

int main()
{
    Clock c;
    // c.hour = 9;              // 错误:hour 是 private,类外不可访问
    c.SetTime(9, 0, 0);        // 合法:通过 public 接口间接操作私有数据
    return 0;
}Code language: JavaScript (javascript)

一般情况下,一个类的数据成员都应该声明为私有成员。这样,内部数据结构就不会对该类以外的其余部分造成影响,程序模块之间的相互作用被降低到最小。

五、保护成员——继承相关

保护类型成员 protected 的性质与私有成员相似,类外部同样不可直接访问。其与私有成员的差别在于继承过程中对产生的新类影响不同:protected 成员在派生类中可以访问,private 成员在派生类中不可访问。

关于 protected 在继承中的具体表现,将在后续继承章节中详细讲解。

六、访问限定符的使用规则

在类的定义中,具有以下特点:

  • 不同访问属性的成员可以按任意顺序出现。
  • 修饰访问属性的关键字(public、private、protected)可以出现多次。
  • 但一个成员只能具有一种访问属性,以后出现的限定符为准直到下一个限定符出现。

以下写法都是正确的:

class Example
{
public:
    int a;              // a 是公有

private:
    int b;              // b 是私有

public:
    void func1();       // func1 是公有

private:
    int c;              // c 是私有
    void helper();      // helper 是私有

protected:
    int d;              // d 是保护
};Code language: PHP (php)

等价写法(推荐,按访问属性分组,每种限定符出现一次):

class Example
{
private:
    int b;
    int c;
    void helper();

protected:
    int d;

public:
    int a;
    void func1();
};Code language: PHP (php)

两种写法在语法上完全等价,但推荐按 private → protected → public 的顺序分组书写,使类的接口一目了然。

七、代码示例

以下代码演示三种访问属性的实际效果:

#include <iostream>
using namespace std;

class Clock
{
private:
    int hour;           // 私有:内部数据
    int minute;
    int second;

    void validate()     // 私有:内部辅助函数
    {
        if (hour < 0 || hour > 23) hour = 0;
        if (minute < 0 || minute > 59) minute = 0;
        if (second < 0 || second > 59) second = 0;
    }

public:
    void SetTime(int h, int m, int s)
    {
        hour = h;
        minute = m;
        second = s;
        validate();     // 类内调用私有成员函数
    }

    void ShowTime()
    {
        if (hour < 10) cout << "0";
        cout << hour << ":";
        if (minute < 10) cout << "0";
        cout << minute << ":";
        if (second < 10) cout << "0";
        cout << second << endl;
    }
};

int main()
{
    Clock c;

    c.SetTime(25, 70, 80);    // 通过公有接口传入非法值
    c.ShowTime();              // 输出 00:00:00(内部 validate 修正)

    // c.hour = 10;            // 错误:hour 是 private
    // c.validate();           // 错误:validate 是 private

    c.SetTime(14, 35, 50);     // 通过公有接口设置合法值
    c.ShowTime();              // 输出 14:35:50

    return 0;
}Code language: PHP (php)

分析:

  • hour、minute、second 和 validate() 都是 private,外部无法直接访问。
  • SetTime 和 ShowTime 是 public,外部通过它们与对象交互。
  • validate() 是私有辅助函数,在 SetTime 内部被调用,负责数据校验。外部不需要知道它的存在,体现了实现隐藏。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注