结论

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

类眼中的自己

类中定义的所有成员,不论是以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--对象的访问权限

在以下的例子中,我们创建了一个简单的类。

下面,我们就来探究一下,对于该类中被不同访问控制修饰符修饰的成员,它们对于该类的对象都有什么样的访问权限。

  1. #include <iostream>
  2. using namespace std;
  3. class CBase
  4. {
  5. private:
  6. int a_base_private;
  7. protected:
  8. int b_base_protected;
  9. public:
  10. int c_base_public;
  11. public:
  12. CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
  13. ~CBase(){}
  14. int getA() const {return a_base_private;} // OK, 类可以访问自身的所有成员
  15. int getB() const {return b_base_protected;} // OK, 类可以访问自身的所有成员
  16. int getC() const {return c_base_public;} // OK, 类可以访问自身的所有成员
  17. };
  18. int main()
  19. {
  20. int tmp;
  21. CBase baseObj;
  22. //baseObj.a_base_private = 1; // KO, 对象不能访问类的private成员
  23. //baseObj.b_base_protected = 1; // KO, 对象不能访问类的protected成员
  24. baseObj.c_base_public = 1; // OK, 对象可以访问类的public成员
  25. tmp = baseObj.getA(); // OK, 对象可以访问类的public成员
  26. tmp = baseObj.getB(); // OK, 对象可以访问类的public成员
  27. tmp = baseObj.getC(); // OK, 对象可以访问类的public成员
  28. }

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

  1. 类可以访问自身的所有成员,不论是private, protected 还是 public。
  2. 对象只能访问类的public成员。

实践2--友元的访问权限

在以上例子的基础上,让我们来考虑一下,对于该类中被不同访问控制修饰符修饰的成员,该类的友元函数和友元类对这些成员都有什么样的访问权限。

  1. #include <iostream>
  2. using namespace std;
  3. class CBase;
  4. class CFriend;
  5. void ClearBaseA(CBase &obj);
  6. class CBase
  7. {
  8. friend CFriend; // 声明CFriend为自己的友元类
  9. friend void ClearBaseB(CBase &obj); // 声明ClearBaseA为自己的友元函数
  10. private:
  11. int a_base_private;
  12. protected:
  13. int b_base_protected;
  14. public:
  15. int c_base_public;
  16. public:
  17. CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
  18. ~CBase(){}
  19. int getA() const {return a_base_private;} // OK, 类可以访问自身的所有成员
  20. int getB() const {return b_base_protected;} // OK, 类可以访问自身的所有成员
  21. int getC() const {return c_base_public;} // OK, 类可以访问自身的所有成员
  22. };
  23. class CFriend
  24. {
  25. private:
  26. CBase obj;
  27. public:
  28. CFriend(){}
  29. ~CFriend(){}
  30. int setBaseA(int f) {obj.a_base_private = f;} // OK, 在友元类中,可以访问Base类的私有成员
  31. int getBaseA() const {return obj.getA();}
  32. };
  33. void ClearBaseB(CBase &obj)
  34. {
  35. obj.b_base_protected = 0; // OK, 在友元函数中,可以访问Base类的保护成员
  36. }
  37. int main()
  38. {
  39. int tmp;
  40. CBase baseObj;
  41. CFriend friendObj;
  42. cout << baseObj.getB() << endl; // 通过构造函数初始化为2
  43. ClearBaseB(baseObj);
  44. cout << baseObj.getB() << endl; // 被友元函数给清0了
  45. cout << friendObj.getBaseA() << endl; // 通过构造函数初始化为1
  46. friendObj.setBaseA(7);
  47. cout << friendObj.getBaseA() << endl; // 被友元类给设置为了7
  48. }

由上例中可以看出,友元可以访问类中的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方式继承出一个新的派生类。

  1. #include <iostream>
  2. using namespace std;
  3. class CBase
  4. {
  5. private:
  6. int a_base_private;
  7. protected:
  8. int b_base_protected;
  9. public:
  10. int c_base_public;
  11. public:
  12. CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
  13. ~CBase(){}
  14. int getA() const {return a_base_private;} // OK, 类可以访问自身的所有成员
  15. int getB() const {return b_base_protected;} // OK, 类可以访问自身的所有成员
  16. int getC() const {return c_base_public;} // OK, 类可以访问自身的所有成员
  17. };
  18. class CDerived:public CBase
  19. {
  20. private:
  21. int x_derived_private;
  22. protected:
  23. int y_derived_protected;
  24. public:
  25. int z_derived_private;
  26. public:
  27. CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
  28. ~CDerived(){}
  29. //void setBaseA(int t){a_base_private = t;} // KO, 派生类中不能访问基类的private成员
  30. void setBaseB(int t){b_base_protected = t;} // OK, 派生类中可以访问基类的protected成员
  31. void setBaseC(int t){c_base_public = t;} // OK, 派生类中可以访问基类的public成员
  32. int getX() const {return x_derived_private;}
  33. int getY() const {return y_derived_protected;}
  34. int getZ() const {return z_derived_private;}
  35. };
  36. int main()
  37. {
  38. CDerived derivedObj;
  39. //derivedObj.a_base_private = 1; // KO, 基类中由private修饰的a_base_private,对派生类是不可见的,即使在派生类中都不能访问,更别提派生类对象了。
  40. //derivedObj.b_base_protected = 1; // KO, 对象不能访问类的protected成员(public方式继承的protected成员,在派生类中仍为protected成员)
  41. derivedObj.c_base_public = 1; // OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
  42. cout << derivedObj.getA() << endl; // OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
  43. derivedObj.setBaseB(8); // OK, 对象可以访问类的public成员
  44. cout << derivedObj.getB() << endl; // OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
  45. derivedObj.setBaseC(9); // OK, 对象可以访问类的public成员
  46. cout << derivedObj.getC() << endl; // OK, 对象可以访问类的public成员(public方式继承的public成员,在派生类中仍为public成员)
  47. }

由以上例子可以看出:

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

protected继承方式

在第一个例子的基础之上,我们通过protected方式继承出一个新的派生类。

  1. #include <iostream>
  2. using namespace std;
  3. class CBase
  4. {
  5. private:
  6. int a_base_private;
  7. protected:
  8. int b_base_protected;
  9. public:
  10. int c_base_public;
  11. public:
  12. CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
  13. ~CBase(){}
  14. int getA() const {return a_base_private;} // OK, 类可以访问自身的所有成员
  15. int getB() const {return b_base_protected;} // OK, 类可以访问自身的所有成员
  16. int getC() const {return c_base_public;} // OK, 类可以访问自身的所有成员
  17. };
  18. class CDerived:protected CBase
  19. {
  20. private:
  21. int x_derived_private;
  22. protected:
  23. int y_derived_protected;
  24. public:
  25. int z_derived_private;
  26. public:
  27. CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
  28. ~CDerived(){}
  29. //void setBaseA(int t){a_base_private = t;} // KO, 派生类中不能访问基类的private成员
  30. void setBaseB(int t){b_base_protected = t;} // OK, 派生类中可以访问基类的protected成员
  31. void setBaseC(int t){c_base_public = t;} // OK, 派生类中可以访问基类的public成员
  32. int getX() const {return x_derived_private;} // OK, 类可以访问自身的所有成员
  33. int getY() const {return y_derived_protected;} // OK, 类可以访问自身的所有成员
  34. int getZ() const {return z_derived_private;} // OK, 类可以访问自身的所有成员
  35. };
  36. int main()
  37. {
  38. CDerived derivedObj;
  39. //derivedObj.a_base_private = 1; // KO, 对象不能访问类的private成员(protected方式继承的private成员,在派生类中不可见)
  40. //derivedObj.b_base_protected = 1; // KO, 对象不能访问类的protected成员(protected方式继承的protected成员,在派生类中仍为protected成员)
  41. //derivedObj.c_base_public = 1; // KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
  42. //cout << derivedObj.getA() << endl; // KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
  43. //cout << derivedObj.getB() << endl; // KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
  44. //cout << derivedObj.getC() << endl; // KO, 对象不可以访问类的protected成员(protected方式继承的public成员,在派生类中成为protected成员)
  45. }

由以上例子可以看出:

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

private继承方式

在第一个例子的基础之上,我们通过private方式继承出一个新的派生类。

  1. #include <iostream>
  2. using namespace std;
  3. class CBase
  4. {
  5. private:
  6. int a_base_private;
  7. protected:
  8. int b_base_protected;
  9. public:
  10. int c_base_public;
  11. public:
  12. CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
  13. ~CBase(){}
  14. int getA() const {return a_base_private;} // OK, 类可以访问自身的所有成员
  15. int getB() const {return b_base_protected;} // OK, 类可以访问自身的所有成员
  16. int getC() const {return c_base_public;} // OK, 类可以访问自身的所有成员
  17. };
  18. class CDerived:private CBase
  19. {
  20. private:
  21. int x_derived_private;
  22. protected:
  23. int y_derived_protected;
  24. public:
  25. int z_derived_private;
  26. public:
  27. CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
  28. ~CDerived(){}
  29. //void setBaseA(int t){a_base_private = t;} // KO, 派生类中不能访问基类的private成员,因为其在派生类中不可见
  30. void setBaseB(int t){b_base_protected = t;} // OK, 派生类中可以访问基类的protected成员
  31. void setBaseC(int t){c_base_public = t;} // OK, 派生类中可以访问基类的public成员
  32. int getX() const {return x_derived_private;} // OK, 类可以访问自身的所有成员
  33. int getY() const {return y_derived_protected;} // OK, 类可以访问自身的所有成员
  34. int getZ() const {return z_derived_private;} // OK, 类可以访问自身的所有成员
  35. };
  36. int main()
  37. {
  38. CDerived derivedObj;
  39. //derivedObj.a_base_private = 1; // KO, (private方式继承的private成员,在派生类中不可见)
  40. //derivedObj.b_base_protected = 1; // KO, (private方式继承的protected成员,在派生类中不可见)
  41. //derivedObj.c_base_public = 1; // KO, (private方式继承的public成员,在派生类中成为不可见)
  42. //cout << derivedObj.getA() << endl; // KO, (private方式继承的public成员,在派生类中不可见)
  43. //cout << derivedObj.getB() << endl; // KO, (private方式继承的public成员,在派生类中不可见)
  44. //cout << derivedObj.getC() << endl; // KO, (private方式继承的public成员,在派生类中不可见)
  45. cout << derivedObj.getX() << endl;
  46. cout << derivedObj.getY() << endl;
  47. cout << derivedObj.getZ() << endl;
  48. }

由以上例子可以看出:

  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. tomcat启动失败怎么回事?

    1.系统环境没有配置好 2.web.xml文件里有错误拼写

  2. SQL——AUTO INCREMENT(字段自增)

    AUTO INCREMENT -- 在新记录插入表中时生成一个唯一的数字.插入表数据时,该字段不需规定值.    在每次插入新记录时,自动地创建主键字段的值.在表中创建一个 auto-incremen ...

  3. js学习零碎只是汇总

    虽然JS是弱类型语言,但也有变量声明,作用域(局部和全局).  1.基础输出语句:    alert();以弹框的方式将括号内的信息输出到页面上,有一个确定按钮.    console.log();常 ...

  4. Docker容器启动时初始化Mysql数据库

    1. 前言 Docker在开发中使用的越来越多了,最近搞了一个Spring Boot应用,为了方便部署将Mysql也放在Docker中运行.那么怎么初始化 SQL脚本以及数据呢? 我这里有两个传统方案 ...

  5. Elasticsearch如何有惊无险地入门,我是用心的

    学习真的是一件令人开心的事情,上次分享了 Redis 入门的文章后,收到了很多小伙伴的鼓励,比如说:"哎呀,不错呀,二哥,通俗易懂,十分钟真的入门了".瞅瞅,瞅瞅,我决定再接再厉, ...

  6. Java获取主板序列号、MAC地址、CPU序列号工具类

    import java.io.File; import java.io.FileWriter; import java.io.BufferedReader; import java.io.IOExce ...

  7. dsPIC33EP单片机的PPS(外设引脚选择)

    利用dsPIC33EP单片机进行can通信的时候用到引脚复用 引脚复用通过查询数据手册: C1RX的寄存器为RPINR26.C1RXR=(设置为需要用到的引脚) 引脚设置为输入(C1RX),TRIS= ...

  8. SourceTree 配置 GitLab

    生成SSH 创建SSH,执行ssh-keygen -t rsa -C "youremail@example.com",会在.ssh目录下生成id_rsa.id_rsa.pub两个私 ...

  9. 判断IP地址的合法性

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

  10. Java实现 LeetCode 605 种花问题(边界问题)

    605. 种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给定一个花坛(表示为一个数组包含0和1,其中0表示没种 ...