首页 > 编程笔记 > C++笔记 阅读:13

C++类成员的访问权限(public、protected和private)

C++ 中,类成员包括类的成员变量和成员函数,它们分别用于描述类的属性和行为。而类成员的访问权限决定了哪些成员是公开的,能被外界访问,也能被类内部访问;哪些成员是私有的,只能在类的内部访问,外界无法访问。就像一个人的钱包,只有他自己能动,别人是不能动的。

大家可能会问,为什么需要对类成员的访问进行控制,而不是让任何人都可以访问呢?这与现实世界中对事物访问的自然限制相呼应。正如我们只能了解自己钱包里有多少钱,而无法知晓他人的财务状况一样,C++ 中的类成员访问控制确保了数据的安全性和封装性。

设想一下,如果钱包里的钱可以被任何人访问,那么它的安全性将无法得到保障。同样,当“人”这个类的属性用成员变量表示时,这些变量的访问也应该受到限制,只能通过类的行为(成员函数)来进行操作,从而对外界隐藏,确保不被未授权的访问或修改。

如果不对访问加以控制,数据可能会被错误地修改,从而威胁到数据的完整性和安全性。因此,为了保护类成员不被不安全的访问,我们必须实施访问控制。通过这种方式,我们可以确保只有适当的操作能够触及敏感数据,从而维护程序的稳定性和可靠性。

C++三种访问权限

在 C++ 中,对类成员的访问控制是通过设置成员的访问权限来实现的。按照访问范围的大小,访问级别分为公有的(public)、受保护的(protected)和私有的(private)三种,如下图所示。


图 1 访问级别

C++公有的访问权限(public)

公有成员通过在成员变量或成员函数前加上 public 关键字来修饰,这表明这些成员的访问不受限制,可以在类的内部和外部被访问。

公有的成员主要作为类与外界交互的接口,它们是类提供给外界的访问点,允许外界通过这些成员与类进行通信和交互。

例如,在 Teacher 类中,如果有一个表示上课行为的 GiveLesson() 成员函数,该函数是类向外界提供的服务之一,因此应该设置为公有的,以便外界可以访问并调用它。这样的设计允许 Teacher 类的用户在需要时请求上课服务,而无须了解类内部的实现细节。

C++受保护的访问权限(protected)

受保护成员用关键字 protected 修饰,其声明格式与 public 型相同,这些成员在类的外部不可见,但可以在类的内部以及所有继承自该类的派生类中访问。

受保护的访问级别主要用于实现属性或方法的继承,允许派生类访问和使用基类的某些成员。

例如,考虑一个 Teacher 类,它包含一个表示姓名的成员变量 m_strName。出于数据封装和安全性的考虑,我们不希望外界能够直接修改教师的姓名。因此,m_strName 应在 Teacher 类内部可见,同时对于任何可能的派生类,如表示大学教师的 Lecturer 类,我们也希望它们能够访问和使用这个姓名属性。在这种情况下,将 m_strName 设置为受保护的成员是最合适的选择。

C++私有的访问权限(private)

私有成员用关键字 private 修饰,其声明格式与 public 类型相同。这些成员只能在类的内部被访问,所有来自类外部的访问都是被禁止的。

私有的访问级别可以确保类的成员被完全隐藏,从而有效地保护类中数据和行为的安全。例如,如果将钱包视为一个类的私有成员,它将不会被外部访问,从而防止潜在的非法访问或修改。

需要说明的是,在使用 class 关键字定义的类中,如果成员没有显式地声明其访问级别,默认为私有成员。这意味着,除非通过类的公共接口,否则外界无法直接访问这些成员。这种默认的访问控制强调了封装的重要性,鼓励开发者通过公共接口来操作类的状态。

相对地,使用 struct 关键字定义的结构体,其默认的访问级别为公有的。这通常用于定义简单的数据结构,其中成员变量和函数可以被直接访问。

C++实例演示三种访问权限

例如下面是一个 Teachar 类,类中结合为各个成员设置了合理的访问权限,更真实地反映了现实的情况:
// 定义访问控制后的 Teacher 类
class Teacher
{
    // 公有成员
    // 外界通过访问这些成员与该类进行交互,以获得类提供的服务
public:
        // 冒号后的变量或者函数都受到它的修饰
        // 构造函数应该是公有的,这样外界才可以利用构造函数创建该类的对象
        Teacher(string strName) : m_strName(strName)
        {
            // ...
        }
        // 老师要为学生们上课,它应该被外界调用,所以这个成员函数是公有的
        void GiveLesson()
        {
            // 在类的内部,可以访问自身的受保护的和私有的成员
            PrepareLesson();    // 先备课,访问受保护成员
            cout << "老师上课。" << endl;
            m_nWallet += 100;  // 一节课钱包增加 100 元,访问私有成员
        }
        // 我们不让别人修改名字,但需要让别人知道我们的名字
        // 对于只可供外界只读访问的成员变量
        // 可以提供一个公有的成员函数供外界对其进行读取访问
        string GetName()
        {
            return m_strName;
        }
    // 受保护成员
    // 不能被外界访问,但可以被自身访问,也可以“遗传”给子类,供子类访问
protected:
        // 自己和子类需要备课,所以不能把“备课”这个功能公开(public)给外界,而是要保护(protected)起来
        void PrepareLesson()
        {
            cout << "老师备课。" << endl;
        }
        // 只有自己可以修改自己的名字,子类也需要这样的属性
        string m_strName;
    // 私有成员
    // 只有自己可以访问,连子类这个“亲儿子”都不能访问
private:
        int m_nWallet;  // 钱包只有自己可以访问,所以设置为私有的
};
设置了访问权限之后,对 Teacher 类对象的成员进行访问时需要特别注意,我们只能访问它的公有成员,试图访问它的受保护或者私有成员,就会被毫不留情地拒之门外:
int main()
{
    // 创建对象时调用类的构造函数
    // 在 Teacher 类中,构造函数是公有的,所以可以直接调用
    Teacher MrChen("Chen");

    // 外部变量,用于保存从对象获得的数据
    string strName;
    // 通过类的公有成员函数,读取并获得类中受保护的成员变量
    strName = MrChen.GetName();

    // 错误:无法直接访问类的受保护和私有的成员
    // 想改我的名字,先要问我答应不答应
    MrChen.m_strName = "WangGang";
    // 想从我钱包中拿走 200 元,那更不行了
    MrChen.m_nWallet -= 200;

    return 0;
}
在主函数中,首先创建了一个 Teacher 类对象,这个创建过程会调用它的构造函数,因此构造函数必须是公有的。

在构造函数中,我们可以访问成员变量 m_strName,虽然它是受保护的,但在类自身的构造函数中可以对它进行修改。同时,为了让外界能够安全地读取该成员变量的值,我们为 Teacher 类添加了一个公有的成员函数 GetName(),通过这个函数,外界可以访问类中受保护的成员以获得必要的数据。

此外,Teacher 类还提供了一个公有的 GiveLesson() 函数,外界可以直接调用这个函数以获得 Teacher 类提供的上课服务。在这个公有成员函数内部,我们也可以访问类中的受保护成员和私有成员。

这些访问都是合理合法的。然而,如果尝试在类的外部直接访问受保护或私有成员,编译器会检测到这种非法访问,并产生编译错误,提示无法访问受保护或私有成员。这样,有了编译器的帮助,小偷就再也不能动我们的钱包了。

通过控制对类成员的访问,我们能够有效地保护数据和行为,防止数据被外界随意修改,同时也限制了外界对类的行为的不合理使用。如果类的某些成员变量因为业务逻辑的需要允许外界访问(例如这里的 m_strName),建议通过公有接口的方法来访问这些成员变量,而不是把这些成员变量设置为公有的。一般情况下,类的成员变量应该设置为受保护的或私有的。

相关文章