C++ 访问修饰符

在 C++ 中定义类时,如果我们把所有成员变量都放在 public: 下面完全公开,类的外部就能随意修改对象里的数据。比如考试成绩本来应该在 0~100 之间,要是外部直接写出 stu.score = -50; 或 stu.score = 999;,类本身根本没法阻止。

为了合理控制类中各个成员的访问范围,C++ 提供了访问修饰符(Access Specifier)。常用的访问修饰符共有 3 种:① public(公有);② private(私有);③ protected(受保护)。

C++ 三种访问修饰符

在刚接触类与对象时,为了方便演示,我们把成员都放在了 public: 下面。实际上,我们可以在 class 的花括号内部按需使用不同的访问修饰符,每个访问修饰符后面都紧跟一个英文冒号(:)。

语法:

class 类名 {
public:
    // 公有成员

private:
    // 私有成员

protected:
    // 受保护成员
};

说明:

当某个访问修饰符出现后,它下方的所有成员都会采用该访问权限,直到遇到下一个访问修饰符或者类定义结束为止。成员变量和成员函数都可以放在任意访问区域中,同一个访问修饰符在一个类里也可以出现多次。

这 3 种访问修饰符的具体作用如下:

  • public(公有):在当前类的内部和类的外部(如 main() 函数中)都可以直接访问,对外提供的成员函数通常放在这里。
  • private(私有):只能在当前类自己的成员函数内部直接访问,在类的外部不能通过对象直接访问,需要保护的成员变量通常放在这里。
  • protected(受保护):在类的外部同样不能直接访问,但允许子类的成员函数直接访问。

在还没有学习类的继承之前,protected 和 private 的表现完全一样,在类的外部都不能直接访问。所以现阶段编写单个类时,小伙伴们只需要重点掌握 public 和 private 这两个就可以了。

另外,大家不要把 private 误解成 “这个变量不能用了”。它只是不允许类外的代码直接读写,在类的内部,我们依然可以通过 public 成员函数去正常读写 private 成员变量。

这 3 种访问修饰符的访问权限如下表所示。

访问修饰符权限
访问修饰符 当前类内部 子类内部 类的外部
public(公有) 可以访问 可以访问 可以访问
protected(受保护) 可以访问 可以访问 不能访问
private(私有) 可以访问 不能访问 不能访问

注意 如果在定义成员时没有写任何访问修饰符,class 中的成员默认是 private,而 struct 中的成员默认是 public。

下面我们通过两个简单的例子,来看看 public 和 private 成员在类内与类外的访问差别。

示例 1:访问公有与私有成员

#include <iostream>
#include <string>

class Student {
public:
    std::string name;    // 公有成员变量

    void showInfo() {
        std::cout << "姓名:" << name << '\n';
        std::cout << "成绩:" << score << '\n';
    }

private:
    int score = 90;      // 私有成员变量
};

int main() {
    Student stu;
    stu.name = "Tom";    // 类外访问 public 成员
    stu.showInfo();      // 调用 public 成员函数
    return 0;
}

运行结果如下。

姓名:Tom
成绩:90

分析:

在这个例子中,name 位于 public: 区域,我们在 main() 函数中可以直接写出 stu.name = "Tom";。而 score 位于 private: 区域,虽然不能在类外直接访问,但类自己的成员函数 showInfo() 依然可以直接读取并打印它。

反过来,如果我们尝试在类的外部直接通过对象去读写 private 私有成员,编译时就会报错。请看下面例子。

示例 2:类外访问私有成员报错

#include <iostream>
#include <string>

class Student {
public:
    std::string name;    // 公有成员变量

private:
    int score = 90;      // 私有成员变量
};

int main() {
    Student stu;
    stu.name = "Tom";    // 正常访问公有成员
    stu.score = 95;      // 错误:不能访问私有成员
    return 0;
}

运行结果如下。

(报错)error: 'int Student::score' is private within this context

分析:

stu.score = 95; 这句代码尝试在 main() 函数中直接修改私有成员 score。由于 private 成员只允许在 Student 类自己的成员函数内部访问,类的外部无权直接读写,所以编译时直接报错,提示 “is private(属于私有成员)”。

使用 set 和 get 函数封装数据

既然 private 成员变量不能在类的外部直接读写,那在实际开发中,我们应该怎么修改和获取它的值呢?

常用的做法是把需要保护的成员变量放进 private 区域,再对外提供公开的 public 成员函数负责读写。习惯上,我们把修改私有成员的函数命名为 setXXX()(如 setScore()),把读取私有成员的函数命名为 getXXX()(如 getScore())。

换成通过成员函数来读写之后,我们就可以在 setScore() 内部加上条件判断,把不合理的数值拦截在外面。下面我们来看一个例子。

示例 3:使用 set 和 get 函数

#include <iostream>

class Student {
private:
    int score = 0;        // 私有成员变量

public:
    // 修改成绩
    void setScore(int newScore) {
        if (newScore >= 0 && newScore <= 100) {
            score = newScore;
        } else {
            std::cout << "成绩无效,需在 0~100 之间\n";
        }
    }

    // 获取成绩
    int getScore() {
        return score;
    }
};

int main() {
    Student stu;

    stu.setScore(95);     // 传入合法成绩
    std::cout << "当前成绩:" << stu.getScore() << '\n';

    stu.setScore(120);    // 传入非法成绩
    std::cout << "当前成绩:" << stu.getScore() << '\n';
    return 0;
}

运行结果如下。

当前成绩:95
成绩无效,需在 0~100 之间
当前成绩:95

分析:

这里 setScore() 和 getScore() 是对外公开的两个 public 成员函数,专门作为外部读写私有成员 score 的通道。

当我们第 1 次调用 stu.setScore(95) 时,95 处于 0~100 之间,score 被正常改为 95。第 2 次调用 stu.setScore(120) 时,120 超出了合理范围,函数给出提示并拒绝修改,因此 score 保持原来的 95 不变。

此外,这种写法还能灵活控制读写权限。比如学生的学号一旦确定就不希望再被外部更改,我们只需要提供读取数据的 get 函数,不提供修改数据的 set 函数,就能实现外部 “只读” 的效果。

像这样把内部数据放进 private 保护起来,只对外提供受控的 public 成员函数来操作数据,就是面向对象编程中常说的 “封装(Encapsulation)”。

常见问题

1. 定义类的时候,所有成员变量都必须设为 private 并编写 set 和 get 函数吗?

不需要。如果我们只是把几项简单数据打包在一起(如二维坐标的 x 和 y),没有任何数值范围限制,直接设为 public 或使用结构体会更简洁。只有当数据不希望被外部随意修改,或者需要在修改时检查合法性时,我们才需要将其设为 private。

2. 在一个类中,public 和 private 区域的先后顺序有规定吗?

C++ 语法本身对先后顺序没有硬性规定,先写 private 还是先写 public 都可以正常编译。有人习惯把 public 写在前面,方便先看清对外接口。也有人习惯先把 private 成员变量列在开头,后面再写 public 成员函数,同一个项目内部保持统一即可。

上一篇:

下一篇:

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号