继承与派生——访问控制

一.知识要点

(一)知识回顾:

基类的成员可以有public、protected、private三种访问属性。基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象,就只能访问该类的公有成员

(二)知识引入:

类的继承方式有public(公有继承)、protected(保护继承)、private(私有继承)三种。不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里的访问分别指两方面:

  • 派生类的新增成员访问从基类继承的成员
  • 派生类外部(非类族内的成员),通过派生类的对象访问从基类继承的成员

下面我们将分别介绍public(公有继承)、protected(保护继承)、private(私有继承)三种。

二.公有继承

(一)知识要点:

1.当类的继承方式为公有继承时,基类的公有成员和保护成员仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问他们。

2.在类之外只能通过派生类的对象访问从基类继承的公有成员。

(二)代码分析

(教材256页)

  1. #include <iostream>
  2. using namespace std;
  3. class Point{
  4. public:
  5. void initPoint(float x=0,float y=0)
  6. {
  7. this->x=x;
  8. this->y=y;
  9. }//构造函数
  10. void move(float movex,float movey)
  11. {
  12. x=x+movex;
  13. y=y+movey;
  14. }
  15. float getx() const {return x;}
  16. float gety() const {return y;}
  17. private:
  18. float x,y;
  19. };
  20. class Rectangle:public Point//派生类的定义
  21. {
  22. public:
  23. void initRectangle(float x,float y,float w,float h)
  24. {
  25. this->w=w;
  26. this->h=h;
  27. initPoint(x,y);//直接使用基类的公有成员函数
  28. }
  29. float geth() const {return h;}
  30. float getw() const {return w;}
  31. private:
  32. float w,h;
  33. };
  34. int main()
  35. {
  36. Rectangle rect;//定义Rectangle类的对象
  37. rect.initPoint(6,6);
  38. cout<<rect.getx()<<endl;
  39. cout<<rect.gety()<<endl;
  40. cout<<"--------"<<endl;
  41. rect.initRectangle(1,2,3,4);
  42. rect.move(2,1);
  43. cout<<rect.getx()<<endl;
  44. cout<<rect.gety()<<endl;
  45. cout<<rect.geth()<<endl;
  46. cout<<rect.getw()<<endl;
  47. return 0;
  48. }

运行结果:

1.修改代码:这里我们让initpoint函数的属性是protected,同时在主函数中不让对象rect调用。

运行结果:

编译时没有报错,说明在公有继承下,派生类Rectangle可以直接访问保护成员。

2.修改代码:这里我们让initpoint函数的属性是protected,同时不改变原始的主函数,即让对象rect调用基类里的两个函数。

运行结果:

编译时发生了错误,错误说initpoint的属性是protected,而对象rect在调用属性为public的move函数时,并没有发生错误说明派生类的对象无法访问基类中的保护成员和私有成员,只可以访问基类中的公有成员。

三.私有继承

(一)知识要点:

1.当类的继承方式为私有继承的时候,基类中的公有成员和保护成员都以私有成员的身份出现在派生类中,派生类的其他成员可以直接访问基类中的公有成员和保护成员。

2.在类族外部通过派生类的对象无法直接基类的公有成员,保护成员和私有成员

(二)代码分析

(以下代码非教材上的代码,教材上私有继承的代码请参考256页)

  1. #include <iostream>
  2. using namespace std;
  3. class point
  4. {
  5. private:
  6. int a;
  7. public:
  8. void inita(int x){a=x;}
  9. int geta(){return a;}
  10. };
  11. class p:private point
  12. {
  13. private:
  14. int b;//派生类的私有数据成员
  15. public:
  16. void init(int x,int y)
  17. {
  18. b=y;
  19. inita(x);//调用基类的成员函数
  20. }
  21. int getb(){return b*geta();}//调用基类的成员函数
  22. };
  23. int main()
  24. {
  25. p op;//创建派生类对象
  26. op.init(2,3);
  27. cout<<op.getb()<<endl;
  28. return 0;
  29. }

运行结果

1.修改代码:

我们在类外通过对象来访问从基类私有继承来的成员inita()

运行结果:

编译发生了错误,证明了类外通过对象是无法访问从基类私有继承来的成员的。那我们该如何修改它呢?

2.修改代码:

我们继续上面的问题,把代码进行如下的修改,我们在派生类中重新声明了基类中的函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来即可。

运行结果:

我们可以看到进行了第二次修改以后,代码可以成功编译了,我们也因此可以解决了类外通过对象来访问基类私有继承来的成员函数的问题了。

3.修改代码:

最后我们进一步完善整个代码成如下的样子

  1. #include"pch.h"
  2. #include <iostream>
  3. using namespace std;
  4. class point
  5. {
  6. private:
  7. int a;
  8. public:
  9. void inita(int x) { a = x; }
  10. int geta() { return a; }
  11. };
  12. class p :private point
  13. {
  14. private:
  15. int b;//派生类的私有数据成员
  16. public:
  17. void init(int x, int y)
  18. {
  19. b = y;
  20. inita(x);//调用基类的成员函数
  21. }
  22. int getb() { return b * geta(); }
  23. void inita(int x) { point::inita(x); }//修改地方2处
  24. int geta(){ return point::geta();}//调用基类的成员函数
  25. };
  26. int main()
  27. {
  28. p op;//创建派生类对象
  29. op.init(2, 3);
  30. op.inita(12);//修改地方1处
  31. cout<<op.getb()<<endl;
  32. cout<<op.geta()<<endl;
  33. return 0;
  34. }

运行结果:

四.保护继承

(一)知识要点:

1.当类的继承方式为保护继承的时候,基类中的公有成员和保护成员都以宝华成员的身份出现在派生类中,派生类的其他成员可以直接访问基类中的公有成员和保护成员。

2.在类的外部通过派生类的对象无法直接访问它们。

(二)代码分析


  1. #include"pch.h"
  2. #include <iostream>
  3. using namespace std;
  4. class point
  5. {
  6. private:
  7. int a;
  8. public:
  9. void inita(int x) { a = x; }
  10. int geta() { return a; }
  11. };
  12. class p :protected point//保护继承
  13. {
  14. private:
  15. int b;//派生类的私有数据成员
  16. public:
  17. void init(int x, int y)
  18. {
  19. b = y;
  20. inita(x);//调用基类的成员函数
  21. }
  22. int getb() { return b * geta(); }
  23. void inita(int x) { point::inita(x); }
  24. int geta(){ return point::geta();}//调用基类的成员函数
  25. };
  26. int main()
  27. {
  28. p op;//创建派生类对象
  29. op.init(3, 4);
  30. cout<<op.getb()<<endl;
  31. op.inita(6);
  32. cout<<op.geta()<<endl;
  33. return 0;
  34. }

运行结果:

代码在私有继承的基础上进行了一点修改,在两方面上的访问属性上,保护继承和私有继承一样,那他们到底有什么区别呢?我们将在文章结尾进行讨论。

五.知识点汇总

  • 基类成员对派生类的可见性
public protected private
公有继承 可见 可见 不可见
私有继承 可见 可见 不可见
保护继承 可见 可见 不可见
  • 基类成员对派生类对象的可见性
public protected private
公有继承 可见 不可见 不可见
私有继承 不可见 不可见 不可见
保护继承 不可见 不可见 不可见
问题思考:
在私有继承和保护继承下,基类的所有成员在派生类中或派生类对象中的访问属性都是相同的,那么两者到底有什么区别?

其实当进行两次派生的时候,他们的区别就出现了。我们假设派生方式是 A->B->C

(a)当B私有继承A的时候,在B类中A类的所有成员都将是私有的了,因此无论C以后再以什么方式来继承B中的成员,在C类中都将无法访问到A类中的成员了。

(b)当B保护继承A时,在B类中A类的公有成员和保护成员都将是保护成员了,这样无论C是以什么方式来继承B中的成员,在C类中都可以访问A中的公有成员和保护成员。

通过上面的讨论,我们发现私有继承之后,基类的成员再也无法在以后的派生类中直接发挥作用,实际上是相当于中止了基类功能的继续派生。

c++第四次作业的更多相关文章

  1. 耿丹CS16-2班第四次作业汇总

    Deadline: 2016-10-13 12:00 作业内容 实验3-1 分别使用while循环.do while循环.for循环求1+2+3+ --+100. 实验3-2 分别使用while循环. ...

  2. 第四次作业——WORDSEARCH小游戏

    “谁想出来的这么缺德的题目啊!!!!”一个声音在我心中回荡 这个题目很早就在课堂上公布了,我和我的小伙伴都惊呆了! 这是个毛?根本无从下手的感觉 总是觉得这个小游戏不是程序能给出答案的,因为我的第一印 ...

  3. 网络1711班 C语言第四次作业批改总结

    网络1711班 C语言第四次作业批改总结 助教有话说(写在前面) 近来,有同学跟老师和助教们反映:博客作业太多太麻烦,而且对编程能力提高似乎没什么帮助?在这里我要谈一谈我的感想. 博客作业的意义? 首 ...

  4. 第四次作业之oop

    第四次作业 四则运算 类 输入类:用户输入题数和答案,语言选择. 生成类:随机数字,运算符,生成表达式. 读取类:读取表达式,计算正确答案. 界面类:选择语言,输出正确题数和答案. 类与类之间是如何进 ...

  5. C#基础第四天-作业答案-Hashtable-list<KeyValuePair>泛型实现名片

    .Hashtable 实现 Hashtable table = new Hashtable(); while (true) { Console.WriteLine("------------ ...

  6. C#基础第四天-作业-Hashtable-list<KeyValuePair>泛型实现名片

    1.名片集: 名片集实现功能:不限定集合实现 增加,查询,查询全部,删除 功能,需求条件: 根据姓名去(查询/删除)某一行数据.指定:姓名,年龄,性别,爱好,联系方式. 采用技术:Hashtable- ...

  7. 《面向对象程序设计》c++第四次作业___calculator plus

    c++第四次作业 Calculator Plus git上的作业展示 Calculator 2.0 SourceCode in Git PS:这次作业orz感谢某同学用windows的dev c++帮 ...

  8. 关于Calculator的第四次作业

    一.魔法传送门: 问题描述:点我点我点我! 仓库地址:点我点我点我! 二.网上资料: sstream的介绍及应用 后缀表达式C++代码 中缀转前缀及后缀方法 C++计算器源代码 三.实现过程: 在看到 ...

  9. 《Java程序设计》十四次作业

    <Java程序设计>十四次作业实验总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 3. 代码量统计 周次 总代码量 新增代码量 总文件数 新增 ...

  10. c++的路上,我坚信,我可以 -----第四次作业体会

    第四次作业 传送门 1.浅谈"新对象"sstream和stack 第四次作业,就是在第三次作业上作修改,上周周末,我刚刚才完成了第三次作业,但是知道了队列如何应用,面对这次的sta ...

随机推荐

  1. PL/SQL无法显示字段可以为NULL还是不能为NULL

    今天用mybatis操作oracle,用PL/SQL看到数据表的字段,明明都是可以为NULL的字段,各个字段都报错,ORA-01400 字段不能为NULL. 后面请教了同事和朋友,才知道这是PL/SQ ...

  2. 【SSH进阶之路】Hibernate映射——多对多关联映射(八)

    上篇博文[SSH进阶之路]Hibernate映射——一对多关联映射(七),我们介绍了一对多关联映射,它是多对多关联映射的基础. 多对多映射是现实生活中最常见的映射,也是最容易理解的映射.废话少说,直接 ...

  3. kubernetes之secret

    Secret解决了密码.token.密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中.Secret可以以Volume或者环境变量的方式使用. Secret类型: Opa ...

  4. Python微服务实践-集成Consul配置中心

    A litmus test for whether an app has all config correctly factored out of the code is whether the co ...

  5. python jieba 词云

    #!/usr/bin/python # coding:utf-8 # 绘制一个<三体>全集词云 # pip install jieba # pip install matplotlib # ...

  6. Appium元素定位难点:tap坐标定位不准确

    tap用法 1.tap是模拟手指点击页面上元素语法有两个参数,第一个是positions,是list类型最多五个点,duration是持续时间,单位毫秒 tap(self, positions, du ...

  7. python递归函数(10)

    一个函数在函数体内部调用自己,这样的函数称为递归函数,递归的次数在python是有限制的,默认递归次数是997次,超过997次会报错:RecursionError. 一.递归函数案例 案例一:计算数字 ...

  8. 算法浅谈之DP悬线法

    悬线法 用途 解决给定矩阵中满足条件的最大子矩阵 做法 用一条线(横竖貌似都行)左右移动直到不满足约束条件或者到达边界 定义 \(left[i][j]\):代表从\((i,j)\)能到达的最左位置 \ ...

  9. sort_buffer_size, Sort_merge_passes关系

    对于事务性工作负载是通常最快这个大小设置为32K,并且也是允许的最小尺寸.您应该谨慎使用它设置为较大的值,因为这可以很容易地降低性能. 如果所有的数据进行排序不适合在指定缓冲区大小的MySQL第一种类 ...

  10. Python解释器安装与环境变量添加

    Python解释器安装与环境变量添加 Python解释器安装(3.6和2.7): www.python.org这个是python解释器的官网,一定要牢记. 鉴于市场上有两种python版本(2和3), ...