C++中类成员的访问控制
结论
首先给出结论,请看下图,看图说话最容易理解了。

类眼中的自己
类中定义的所有成员,不论是以public, protected还是private修饰,对类自身而言,它们都是可见的。
对象眼中的类
站在类的对象的角度去观察类,这时,只有类中的public成员是可见的。而其中的protected和private成员对对象而言,是不可见的。
友元眼中的类
站在友元的角度,类中所有的成员,不论是以public, protected还是private修饰,对友元而言,它们都是可见的。
派生类眼中的基类
派生类只能看见基类中的public和protected成员。而基类中的private成员,对于派生类而言,是不可见的。有一点必须注意,这里的public, protected和private均是指基类在被继承之后所呈现出来的成员访问权限。
下表展示了不同的继承方式,对基类中各成员访问权限的影响。
| 基类中所用访问控制修饰符 | public继承 | protected继承 | private继承 | 
|---|---|---|---|
| public | public | protected | private | 
| protected | protected | protected | private | 
| private | private | private | private | 
所以,在派生类中,对于基类中的各成员的访问权限,我们可以按如下步骤去判断:
- 根据上表,确定基类中各成员在被继承之后访问权限的变化。
- 根据“ 派生类只能看见基类中的public和protected成员” 这一点来确定哪些基类成员可以被访问。
好了,到这里为止,结论就全部说完了。
接下来全是对结论的验证,所以,无需从头至尾浏览,请有针对性的选择查看。
引入三种访问控制符
C++中,存在三种访问控制修饰符,它们分别是:
- public // 公有成员
- protected // 保护成员
- private // 私有成员
术语
为了使文章容易理解,我们对以下术语作出说明:
- 对象: 与类相对,对象是类的实例。
- 派生类:与基类相对,派生类就是子类。
- 继承:继承与派生是一个意思。继承偏重指出此过程中不变的部分,而派生的意思则更偏向于在原有基础上所新增加的部分。
- 成员:类中成员变量和成员函数的统称。
实践1--对象的访问权限
在以下的例子中,我们创建了一个简单的类。
下面,我们就来探究一下,对于该类中被不同访问控制修饰符修饰的成员,它们对于该类的对象都有什么样的访问权限。
#include <iostream>
using namespace std;
class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}
	int getA() const {return a_base_private;}		// OK, 类可以访问自身的所有成员
	int getB() const {return b_base_protected;}		// OK, 类可以访问自身的所有成员
	int getC() const {return c_base_public;}		// OK, 类可以访问自身的所有成员
};
int main()
{
	int tmp;
	CBase baseObj;
	
	//baseObj.a_base_private = 1;			// KO, 对象不能访问类的private成员
	//baseObj.b_base_protected = 1;			// KO, 对象不能访问类的protected成员
	baseObj.c_base_public = 1;				// OK, 对象可以访问类的public成员
	
	tmp = baseObj.getA();		// OK, 对象可以访问类的public成员
	tmp = baseObj.getB();		// OK, 对象可以访问类的public成员
	tmp = baseObj.getC();		// OK, 对象可以访问类的public成员
}
从以上实践中可以得出以下结论:
- 类可以访问自身的所有成员,不论是private, protected 还是 public。
- 对象只能访问类的public成员。
实践2--友元的访问权限
在以上例子的基础上,让我们来考虑一下,对于该类中被不同访问控制修饰符修饰的成员,该类的友元函数和友元类对这些成员都有什么样的访问权限。
#include <iostream>
using namespace std;
class CBase;
class CFriend;
void ClearBaseA(CBase &obj);
class CBase
{
friend 	CFriend;							// 声明CFriend为自己的友元类
friend 	void ClearBaseB(CBase &obj);		// 声明ClearBaseA为自己的友元函数
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}
	int getA() const {return a_base_private;}		// OK, 类可以访问自身的所有成员
	int getB() const {return b_base_protected;}		// OK, 类可以访问自身的所有成员
	int getC() const {return c_base_public;}		// OK, 类可以访问自身的所有成员
};
class CFriend
{
private:
	CBase obj;
	
public:
	CFriend(){}
	~CFriend(){}
	
	int setBaseA(int f) {obj.a_base_private = f;}		// OK, 在友元类中,可以访问Base类的私有成员
	int getBaseA() const {return obj.getA();}
};
void ClearBaseB(CBase &obj)
{
	obj.b_base_protected = 0;			// OK, 在友元函数中,可以访问Base类的保护成员
}
int main()
{
	int tmp;
	CBase baseObj;
	CFriend friendObj;
	
	cout << baseObj.getB() << endl;		// 通过构造函数初始化为2
	ClearBaseB(baseObj);
	cout << baseObj.getB() << endl;		// 被友元函数给清0了
	
	cout << friendObj.getBaseA() << endl;	// 通过构造函数初始化为1
	friendObj.setBaseA(7);
	cout << friendObj.getBaseA() << endl;	// 被友元类给设置为了7
}
由上例中可以看出,友元可以访问类中的private和protected成员,对于public成员,当然更是可以访问的了,虽然以上例子中并没有验证这一点。
所以,我们可以得出以下结论:
- 友元函数或友元类可以访问类中的所有成员。
 另外,关于友元还有一点需要注意。在友元中,只能通过对象来访问声明友元的类成员。
小结
现在,让我们换一个角度,通过以下表格总结一下。
| 访问控制修饰符 | 类 | 对象 | 友元 | 
|---|---|---|---|
| public | 可见 | 可见 | 可见 | 
| protected | 可见 | 不可见 | 可见 | 
| private | 可见 | 不可见 | 可见 | 
引入三种继承方式
在C++中,在继承的过程中,有以下三种继承方式,它们分别是:
- public (公有继承)
- protected (保护继承)
- private (私有继承)
 这三个关键字与之前的三种访问控制修饰符刚好相同,但在这里,它们有不同的意义。
- 对于public继承,基类中的成员的访问控制修饰符不作任何改动,原样继承到派生类中。
 也就是说,基类中的public成员,在被以public方式继承之后,仍然是基类的public成员;基类中的protected成员,仍然是protected成员;基类中的private成员,它仍然是private成员。注意,在继承之后,基类中的private成员对于派生类是不可见的。
- 对于protected继承,基类中的public成员,在被以protected方式继承之后,它变成了基类的protected成员;基类中的protected成员,则仍然是protected成员;基类中的private成员,则仍然是private成员。注意,在继承之后,基类中的private成员对于派生类是不可见的。
- 对于private继承,基类中的public和protected和private成员,在被以private方式继承之后,在基类中均成为了private成员;而基类中的private成员,对派生类不可见。
public继承方式
在第一个例子的基础之上,我们通过public方式继承出一个新的派生类。
#include <iostream>
using namespace std;
class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}
	int getA() const {return a_base_private;}		// OK, 类可以访问自身的所有成员
	int getB() const {return b_base_protected;}		// OK, 类可以访问自身的所有成员
	int getC() const {return c_base_public;}		// OK, 类可以访问自身的所有成员
};
class CDerived:public CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}
	
	//void setBaseA(int t){a_base_private = t;}		// KO, 派生类中不能访问基类的private成员
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生类中可以访问基类的protected成员
	void setBaseC(int t){c_base_public = t;}		// OK, 派生类中可以访问基类的public成员	
	int getX() const {return x_derived_private;}
	int getY() const {return y_derived_protected;}
	int getZ() const {return z_derived_private;}
};
int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, 基类中由private修饰的a_base_private,对派生类是不可见的,即使在派生类中都不能访问,更别提派生类对象了。 
	//derivedObj.b_base_protected = 1;			// KO, 对象不能访问类的protected成员(public方式继承的protected成员,在派生类中仍为protected成员)
	derivedObj.c_base_public = 1;				// OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
	
	cout << derivedObj.getA() << endl;		// OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
	derivedObj.setBaseB(8);					// OK, 对象可以访问类的public成员
	cout << derivedObj.getB() << endl;		// OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
	derivedObj.setBaseC(9);					// OK, 对象可以访问类的public成员
	cout << derivedObj.getC() << endl;		// OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
}
由以上例子可以看出:
- 基类中的private, protected, public成员,经由public继承之后,在派生类中分别为不可见private, protected,public成员。
- 派生类中不能访问基类的private成员,但可以访问基类的private和protected成员。
protected继承方式
在第一个例子的基础之上,我们通过protected方式继承出一个新的派生类。
#include <iostream>
using namespace std;
class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}
	int getA() const {return a_base_private;}		// OK, 类可以访问自身的所有成员
	int getB() const {return b_base_protected;}		// OK, 类可以访问自身的所有成员
	int getC() const {return c_base_public;}		// OK, 类可以访问自身的所有成员
};
class CDerived:protected CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}
	//void setBaseA(int t){a_base_private = t;}		// KO, 派生类中不能访问基类的private成员
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生类中可以访问基类的protected成员
	void setBaseC(int t){c_base_public = t;}		// OK, 派生类中可以访问基类的public成员
	int getX() const {return x_derived_private;}	// OK, 类可以访问自身的所有成员
	int getY() const {return y_derived_protected;}	// OK, 类可以访问自身的所有成员
	int getZ() const {return z_derived_private;}	// OK, 类可以访问自身的所有成员
};
int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, 对象不能访问类的private成员(protected方式继承的private成员,在派生类中不可见)
	//derivedObj.b_base_protected = 1;			// KO, 对象不能访问类的protected成员(protected方式继承的protected成员,在派生类中仍为protected成员)
	//derivedObj.c_base_public = 1;				// KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
	
	//cout << derivedObj.getA() << endl;		// KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
	//cout << derivedObj.getB() << endl;		// KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
	//cout << derivedObj.getC() << endl;		// KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
}
由以上例子可以看出:
- 基类中的private, protected, public成员,经由protected继承之后,在派生类中分别为不可见private, protected,protected成员。
- 派生类中不能访问基类的private成员,但可以访问基类的private和protected成员。
private继承方式
在第一个例子的基础之上,我们通过private方式继承出一个新的派生类。
#include <iostream>
using namespace std;
class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}
	int getA() const {return a_base_private;}		// OK, 类可以访问自身的所有成员
	int getB() const {return b_base_protected;}		// OK, 类可以访问自身的所有成员
	int getC() const {return c_base_public;}		// OK, 类可以访问自身的所有成员
};
class CDerived:private CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}
	//void setBaseA(int t){a_base_private = t;}		// KO, 派生类中不能访问基类的private成员,因为其在派生类中不可见
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生类中可以访问基类的protected成员
	void setBaseC(int t){c_base_public = t;}		// OK, 派生类中可以访问基类的public成员
	int getX() const {return x_derived_private;}	// OK, 类可以访问自身的所有成员
	int getY() const {return y_derived_protected;}	// OK, 类可以访问自身的所有成员
	int getZ() const {return z_derived_private;}	// OK, 类可以访问自身的所有成员
};
int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, (private方式继承的private成员,在派生类中不可见)
	//derivedObj.b_base_protected = 1;			// KO, (private方式继承的protected成员,在派生类中不可见)
	//derivedObj.c_base_public = 1;				// KO, (private方式继承的public成员,在派生类中成为不可见)
	
	//cout << derivedObj.getA() << endl;		// KO, (private方式继承的public成员,在派生类中不可见)
	//cout << derivedObj.getB() << endl;		// KO, (private方式继承的public成员,在派生类中不可见)
	//cout << derivedObj.getC() << endl;		// KO, (private方式继承的public成员,在派生类中不可见)
	
	cout << derivedObj.getX() << endl;
	cout << derivedObj.getY() << endl;
	cout << derivedObj.getZ() << endl;
}
由以上例子可以看出:
- 基类中的private, protected, public成员,经由private继承之后,在派生类中均不可见。
- 派生类中不能访问基类的private成员,但可以访问基类的private和protected成员。
小结
- 不论何种继承方式,派生类都不能访问基类的private成员,它只能访问基类的public和protected成员。
- 三种继承方式对不同访问控制符修饰的成员的影响如下表所示。
| 基类中所用访问控制修饰符 | public继承 | protected继承 | private继承 | 
|---|---|---|---|
| public | public | protected | private | 
| protected | protected | protected | private | 
| private | private | private | private | 
总结
- 友元和类一样,可以访问类的所有成员。
- 对象只能访问类的public成员。
- 派生类只能访问基类的public和protected成员,而不能访问基类的private成员。
- 对于派生出来的类,首先根据继承方式,确定基类各成员在经指定的继承方式继承后的访问控制权限(经继承后基类各成员是变成了public,protected还是private),然后根据第1、2、3点对各成员进行访问。
- 经继承后,基类中的成员会根据继承方式,对各成员的访问控制符进行修改。修改之后,基类中的private成员对派生类不可见。
参考文献
- 郑莉 C++语言程序设计 类的友元 继承方式简介及公有继承 私有继承和保护继承
- 菜鸟教程 C++ 类访问修饰符
 

 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号