结论

首先给出结论,请看下图,看图说话最容易理解了。

类眼中的自己

类中定义的所有成员,不论是以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

所以,在派生类中,对于基类中的各成员的访问权限,我们可以按如下步骤去判断:

  1. 根据上表,确定基类中各成员在被继承之后访问权限的变化。
  2. 根据“ 派生类只能看见基类中的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成员
}

从以上实践中可以得出以下结论:

  1. 类可以访问自身的所有成员,不论是private, protected 还是 public。
  2. 对象只能访问类的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成员,当然更是可以访问的了,虽然以上例子中并没有验证这一点。

所以,我们可以得出以下结论:

  1. 友元函数或友元类可以访问类中的所有成员。

    另外,关于友元还有一点需要注意。在友元中,只能通过对象来访问声明友元的类成员。

小结

现在,让我们换一个角度,通过以下表格总结一下。

访问控制修饰符 对象 友元
public 可见 可见 可见
protected 可见 不可见 可见
private 可见 不可见 可见

引入三种继承方式

在C++中,在继承的过程中,有以下三种继承方式,它们分别是:

  • public (公有继承)
  • protected (保护继承)
  • private (私有继承)

    这三个关键字与之前的三种访问控制修饰符刚好相同,但在这里,它们有不同的意义。
  1. 对于public继承,基类中的成员的访问控制修饰符不作任何改动,原样继承到派生类中。

    也就是说,基类中的public成员,在被以public方式继承之后,仍然是基类的public成员;基类中的protected成员,仍然是protected成员;基类中的private成员,它仍然是private成员。注意,在继承之后,基类中的private成员对于派生类是不可见的。
  2. 对于protected继承,基类中的public成员,在被以protected方式继承之后,它变成了基类的protected成员;基类中的protected成员,则仍然是protected成员;基类中的private成员,则仍然是private成员。注意,在继承之后,基类中的private成员对于派生类是不可见的。
  3. 对于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成员)
}

由以上例子可以看出:

  1. 基类中的private, protected, public成员,经由public继承之后,在派生类中分别为不可见private, protected,public成员。
  2. 派生类中不能访问基类的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成员)
}

由以上例子可以看出:

  1. 基类中的private, protected, public成员,经由protected继承之后,在派生类中分别为不可见private, protected,protected成员。
  2. 派生类中不能访问基类的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;
}

由以上例子可以看出:

  1. 基类中的private, protected, public成员,经由private继承之后,在派生类中均不可见。
  2. 派生类中不能访问基类的private成员,但可以访问基类的private和protected成员。

小结

  • 不论何种继承方式,派生类都不能访问基类的private成员,它只能访问基类的public和protected成员。
  • 三种继承方式对不同访问控制符修饰的成员的影响如下表所示。
基类中所用访问控制修饰符 public继承 protected继承 private继承
public public protected private
protected protected protected private
private private private private

总结

  1. 友元和类一样,可以访问类的所有成员。
  2. 对象只能访问类的public成员。
  3. 派生类只能访问基类的public和protected成员,而不能访问基类的private成员。
  4. 对于派生出来的类,首先根据继承方式,确定基类各成员在经指定的继承方式继承后的访问控制权限(经继承后基类各成员是变成了public,protected还是private),然后根据第1、2、3点对各成员进行访问。
  5. 经继承后,基类中的成员会根据继承方式,对各成员的访问控制符进行修改。修改之后,基类中的private成员对派生类不可见。

参考文献

C++中类成员的访问控制的更多相关文章

  1. PHP中类成员的访问控制

    类成员访问控制: 1.public 默认的,任何地方都可以访问,类内,类外,子类中 2.protected 受保护的,对外是封闭的,但是类内部和子类可以访问 3.private  私有的,仅限于本类中 ...

  2. Delphi XE中类成员的访问权限(新增了strict private和strict protected,还有automated)

    Delphi XE中类成员的访问权限共提供了6个关键词来用于限定访问权限:public.private.protected.published.automated strict private . s ...

  3. 鸡啄米:C++编程之十三学习之类与对象,类的声明,成员的访问控制

    1. 本次学习鸡啄米课程第13篇,把比较重要的学习记录下来,以敦促自己更好的学习.推荐他们的网址学习:http://www.jizhuomi.com/school/c/97.html 2. 在面向过程 ...

  4. this、static、main方法、静态代码块、final关键字、Runtime类、Cloneable类、类成员的访问控制权限、异常体系

    this表示当前对象,用在方法内部,当某对象调用该方法时,该方法中的this就代表调用该方法的对象: static关键字: 修饰类属性后,该属性就成为该类所有实例的公共属性,修改该属性值,所有的实例的 ...

  5. Java中类成员变量初始化顺序

    一. 定义处默认初始化vs构造函数中初始化 java中类成员变量支持在声明处初始化,也可以在构造函数中初始化,那么这两者有什么区别呢?看下面例子 public class FieldsInit { p ...

  6. C++中类成员变量在初始化列表中的初始化顺序

    引子:我们知道,C++中类成员变量的初始化顺序与其在类中的声明顺序是有关的. 先看代码: class TestClass1 { public: TestClass1() { cout << ...

  7. C++中类成员使用前需要初始化的重要性

    今天写程序的时候,创建了一个结构体: struct BufferObj { char* buf; int bufLen; SOCKADDR_STORAGE addr; int addrLen; str ...

  8. C++中类成员函数作为回调函数

    注:与tr1::function对象结合使用,能获得更好的效果,详情见http://blog.csdn.net/this_capslock/article/details/38564719 回调函数是 ...

  9. C#中类成员的执行顺序

    先进行细分: 类的成员分为:字段.属性.方法.构造方法 成员的修饰符:静态成员.实例成员 层次结构:父类.子类 先不考虑继承关系,执行顺序为: 静态字段静态构造方法实例字段实例构造方法属性和方法是在调 ...

随机推荐

  1. Jmeter基础-下载与安装

    jmeter下载与安装 下载网址:http://jmeter.apache.org/download_jmeter.cgi windows点击下载zip文件 该版本需要JDK1.8及以上版本 免安装, ...

  2. Java——关键字和保留字

    Java关键字50个 abstract assert boolean break byte case catch char class const continue default do double ...

  3. pip命令报错“no perl script found in input”

    windows10,命令行下使用pip命令时报错,python运行成功,且环境变量已经设置,可能是更新了新版本pip所导致 解决方案: 一.使用pip.exe+命令可解决这个问题 二.进行python ...

  4. TopK (MinK) 实现

    概述:基于快排原理找到最小的K个元素,属于Top K问题.注意,使用快排原理找前K小问题不需要对整个数组进行O(nlogn)的排序.我们只要找K所在的区间进行递归调用,即每次只要对数据的一半进行递归调 ...

  5. Spring MVC基于注解@Controller和@RequestMapping开发的一个例子

    1.创建web项目 2.在springmvc的配置文件中指定注解驱动,配置扫描器 在 Spring MVC 中使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC ...

  6. [Python基础]007.字符串

    字符串 内建操作 字符串长度 大小写变换 去空格或其他 连接字符串 查找替换 分割 判断 内建操作 字符串长度 len 代码 s = 'abcd' print len(s) 大小写变换 lower 小 ...

  7. Bank3

    Account: package banking3; //账户 public class Account { private double balance;// 账户余额 public Account ...

  8. 关于Backus-Naur Form巴克斯诺尔范式和扩展巴克斯范式的知识点和相关词语中英文对照

    巴克斯诺尔范式的相关词语中英文对照和知识点 syntax 语法 强调的是编程语言的组形式,例如一个句子中会包含表达式.陈述还有各种单元等等 semantics 语义 强调的是这个编程语言的实际含义,例 ...

  9. 关于同一密码使用generate_password_hash生成不同的密码散列值

    在python的 werkzeug.security 库中有两个函数generate_password_hash与check_password_hash用于对密码明文生成散列值以及检查密码是否与提供的 ...

  10. 判断IP地址的合法性

    每台计算机都有独一无二的编号,称为ip地址,每个合法的ip地址由‘.’分隔开的4个数字组成,每个数字的取值范围为0--255 输入一个字符串,判断其是否为合法的IP地址,若是输出‘YES’,否则输出‘ ...