C++类成员的访问范围(C++ private、public、protected)
在类的定义中,可以用 private、public 和 protected 三种关键字来指定成员可被访问的范围。
private:用来指定私有成员。一个类的私有成员,不论是成员变量还是成员函数,都只能在该类的成员函数内部才能被访问。
public:用来指定公有成员。一个类的公有成员在任何地方都可以被访问。
protected:用来指定保护成员。这需要等介绍“继承”之后再解释。
三种关键字出现的次数和先后次序都没有限制。成员变量的可访问范围由离它前面最近的那个访问范围说明符决定。
如果某个成员前面没有访问范围说明符,则对 class 来说,该成员默认地被认为是私有成员;对 struct 来说,该成员默认地被认为是公有成员。例如:
下面的程序可以说明公有成员和私有成员的区别。假设一个企业员工管理程序的一小部分代码如下:
私有成员只能在成员函数内部访问,因此第 14 行和第 17 行没有问题,这两条语句都是在访问函数所作用的那个对象的 szName 私有成员。另外,类的成员函数内部可以访问任何同类对象的私有成员。
所谓成员函数内部,指的就是成员函数的函数体内部。main 函数中的语句,如第 26 行,当然就不在 CEmployee 的成员函数内部,因此该行试图访问 e 这个对象的 szName 私有成员变量就会导致编译错误。
而第 27 行虽然也不属于 CEmployee 类的成员函数内部,但其访问的是对象 e 的公有成员 setName,因此没有问题。同理,第 28 行也没有问题。
在 CEmployee 类的成员函数外面,若要访问 CEmployee 对象的 szName 私有成员变量,不能直接访问,只能通过两个成员函数 setName 和 getName 间接进行访问。
以上面的企业员工管理程序为例,如果 szName 不是私有的,那么整个程序中可能会有很多类似于第 26 行
如果将 szName 变为私有的,则除了 CEmployee 类的成员函数内部,其他地方不可能出现第 26 行那样对 szName 直接访问的语句,所有对 szName 的访问都是通过成员函数进行的。例如:
可见,“隐藏”有利于程序的修改。
“隐藏”机制还可以避免对对象的不正确操作。有的成员函数只是设计用来让同类的成员函数调用的,并不希望对外开放,因此就可以将它们声明为私有的,隐藏起来。
现代软件开发绝大多数是合作完成的,一个程序员设计了一个类,可能被许多程序员使用。在设计类的时候,应当尽可能隐藏使用者不需要知道的实现细节,只留下必要的接口(即一些成员函数)来对对象进行操作,这样能够避免类的使用者随意使用成员函数和成员变量而导致错误。
就像数字照相机的设计者会用外壳将内部的电路全部封装隐藏起来,用户不需要知道数字照相机的具体工作原理以及其中有哪些器件,只要能通过设计者留下的接口,即外壳上的各种按钮来使用照相机即可。如果把内部的电路、器件、开关都暴露给用户,那么外行用户很可能会把照相机弄坏。
private:用来指定私有成员。一个类的私有成员,不论是成员变量还是成员函数,都只能在该类的成员函数内部才能被访问。
public:用来指定公有成员。一个类的公有成员在任何地方都可以被访问。
protected:用来指定保护成员。这需要等介绍“继承”之后再解释。
三种关键字出现的次数和先后次序都没有限制。成员变量的可访问范围由离它前面最近的那个访问范围说明符决定。
如果某个成员前面没有访问范围说明符,则对 class 来说,该成员默认地被认为是私有成员;对 struct 来说,该成员默认地被认为是公有成员。例如:
class A { int m, n; public: int a, b; int func1(); private: int c, d; void func2(); public: char e; int f; int func3(); };在上面的类 A 中,成员 a、b 和 func1 是公有的,c、d 和 func2 是私有的,e、f 和 func3 又是公有的。m 和 n 没有指定可访问范围,则是私有的。如果把 class 换成 struct,那么 m 和 n 就是公有的。
下面的程序可以说明公有成员和私有成员的区别。假设一个企业员工管理程序的一小部分代码如下:
#include <iostream> #include <cstring> using namespace std; class CEmployee { private: char szName[30]; //名字 public: int salary; //工资 void setName(char* name); void getName(char* name); void averageSalary(CEmployee el, CEmployee e2); }; void CEmployee::setName(char* name) { strcpy(szName, name); //ok } void CEmployee::getName(char* name) { strcpy(name, szName); //ok } void CEmployee::averageSalary(CEmployee el, CEmployee e2) { salary = (el.salary + e2.salary) / 2; } int main() { CEmployee e; strcpy(e.szName, "Tom1234567889"); //编译出错,不能访问私有成员 e.setName("Tom"); //ok e.salary = 5000; //ok return 0; }在上面的程序中,szName 是私有成员,其他成员都是公有的。
私有成员只能在成员函数内部访问,因此第 14 行和第 17 行没有问题,这两条语句都是在访问函数所作用的那个对象的 szName 私有成员。另外,类的成员函数内部可以访问任何同类对象的私有成员。
所谓成员函数内部,指的就是成员函数的函数体内部。main 函数中的语句,如第 26 行,当然就不在 CEmployee 的成员函数内部,因此该行试图访问 e 这个对象的 szName 私有成员变量就会导致编译错误。
而第 27 行虽然也不属于 CEmployee 类的成员函数内部,但其访问的是对象 e 的公有成员 setName,因此没有问题。同理,第 28 行也没有问题。
在 CEmployee 类的成员函数外面,若要访问 CEmployee 对象的 szName 私有成员变量,不能直接访问,只能通过两个成员函数 setName 和 getName 间接进行访问。
“隐藏”的作用
设置私有成员的机制叫作“隐藏”。“隐藏”的一个目的就是强制对成员变量的访问一定要通过成员函数进行。这样做的好处是,如果以后修改了成员变量的类型等属性,只需要更改成员函数即可;否则,所有直接访问成员变量的语句都需要修改。以上面的企业员工管理程序为例,如果 szName 不是私有的,那么整个程序中可能会有很多类似于第 26 行
strcpy(man1, szName, "Tom1234567889");
这样的语句。假设需要将该程序移植到内存空间紧张的手持设备上,希望将 CEmployee 类的成员变量 szName 改为 char szName[5],以便节约空间,那么所有这样的语句都要找出来检查一番并修改,以防止数组越界。这显然很麻烦。如果将 szName 变为私有的,则除了 CEmployee 类的成员函数内部,其他地方不可能出现第 26 行那样对 szName 直接访问的语句,所有对 szName 的访问都是通过成员函数进行的。例如:
e.setName("Tom12345678909887");
就算 szName 的长度变短了,上面的语句也不需要修改,只要修改 setName 成员函数,在其中去掉超长的部分,确保数组不越界就可以了。可见,“隐藏”有利于程序的修改。
“隐藏”机制还可以避免对对象的不正确操作。有的成员函数只是设计用来让同类的成员函数调用的,并不希望对外开放,因此就可以将它们声明为私有的,隐藏起来。
现代软件开发绝大多数是合作完成的,一个程序员设计了一个类,可能被许多程序员使用。在设计类的时候,应当尽可能隐藏使用者不需要知道的实现细节,只留下必要的接口(即一些成员函数)来对对象进行操作,这样能够避免类的使用者随意使用成员函数和成员变量而导致错误。
就像数字照相机的设计者会用外壳将内部的电路全部封装隐藏起来,用户不需要知道数字照相机的具体工作原理以及其中有哪些器件,只要能通过设计者留下的接口,即外壳上的各种按钮来使用照相机即可。如果把内部的电路、器件、开关都暴露给用户,那么外行用户很可能会把照相机弄坏。