• 面向对象——继承(c++)


    目录

    一、什么是继承

    1.1、继承是什么

    1.2、继承的概念与定义

    二、继承的设计

    由基类派生出派生类的定义一般形式为:

    举例:

    三、struct(结构体) 与 class(类) 在继承中的区别

    四、c++继承的理解

    4.1继承父类的派生类成员属性

    4.1.1设计代码

    4.1.2内存分配

    4.1.3能否理解为派生类继承后拥有基类的成员属性?

    4.2公有继承、私有继承、保护继承(派生类类成员函数访问)

    4.2.1问题分析及代码设计

    4.2.2 三种继承(类成员函数访问)

    保护继承:

    私有继承:

    4.2.3总结:

    4.3三种继承(继承的对象和成员直接关系)

    4.3.1问题分析、代码设计

    4.3.2结果分析

    ​4.3.3总结

    4.4三种继承(外部函数和成员函数的区别)

    4.4.1问题分析、代码设计

    4.4.2结果分析

    4.4.3总结

    4.5三层继承关系

    4.5.1问题分析、代码设计

    4.5.2结果分析

     4.5.3总结

    4.6继承中的同名隐藏

    4.6.1问题分析、代码设计;

    4.6.2结果分析

     4.6.3总结

    4.7切片问题

    4.7.1问题分析、代码设计

    4.7.2结果显示

     4.7.3总结

    4.7.4 问题一:那如果把上述代码中的参数列表初始化改为如下;会不会改变?

    4.7.5思考:基类能否给派生类成员赋值?

    4.8拷贝构造、赋值函数是否具有继承性

    4.8拷贝构造函数分析

    4.8.2代码分析

    4.8.3怎样才能调用基类的拷贝构造?


    一、什么是继承

    1.1、继承是什么

    继承(inheritance) 机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础,上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构。体现了由简单到复杂的认识过程。百度百科(继承)

    1.2、继承的概念与定义

        通过继承(inheritance)的机制可对类(class) 分层,提供类型/子类型的关系。
            C+ +通过类派生(class derivation)的机制来支持继承。被继承的类称为基类(base class)或超类(superclass),新产生的类为派生类(derived class)或子类(subclass) 。基类和派生类的集合称作类继承层次结构(hierarchy) 。

    二、继承的设计

    由基类派生出派生类的定义一般形式为:

    class 派生类名:访问限定符  基类名1《,访问限定符  基类名2,.....访问限定符  基类名n
    {
       private:
          // 成员表1     
          // 派生类增加或替代的私有成员
       public:
          // 成员表2    
          // 派生类增加或替代的公有成员
       protected:
          // 成员表3    
          // 派生类增加或替代的保护成员
    };//分号不可少

    举例:

    1. class Person {
    2. private:
    3. char IdPerson[19]; // 编号
    4. char Name[20]; //姓名
    5. public:
    6. Person() { IdPerson[0] = '\0'; Name[0] = '\0'; }
    7. Person(char* id, char* name) {
    8. strcpy(IdPerson, id);
    9. strcpy(Name, name);
    10. }
    11. ~Person(){};
    12. void PrintPersonInfo()const {
    13. cout << "编号" << IdPerson << '\n' << "姓名:" << Name << endl;
    14. }
    15. };
    16. class Student : public Person
    17. {
    18. private:
    19. char snum[10];
    20. float grade;
    21. public:
    22. Student():Person(){}
    23. Student(char* id, char* name, char* sn) :Person(id, name) {
    24. strcpy(snum, sn);
    25. grade = 0.0;
    26. }
    27. ~Student(){}
    28. void PrintStudentInfo()const {
    29. PrintPersonInfo();
    30. cout << "学号: " << snum << "\n" << "成绩: " << grade << endl;
    31. }
    32. void SetGrade(float ft) {
    33. grade = ft;
    34. }
    35. };

    总结:派生反映了事物之间的联系,事物的共性与个性之间的关系。派生与独立设计若干相关的类, 前者工作量少,复的部分可以从基类继承来,不需要单独编程。

    三、struct(结构体) 与 class(类) 在继承中的区别

    • 1)struct默认属性是公有的,class默认属性是私有的。
    • 2)struct设计一个类型是把它当作一个集合来看待,class设计类型的时候把它当作一个对象来看待。
    • 3)派生类struct在继承的时候缺省访问限定符,就会默认为公有继承,即使继承的是一个class类,也是默认公有继承。如果是派生类class在继承的时候,缺省访问限定符,默认是私有继承。

    四、c++继承的理解

    4.1继承父类的派生类成员属性

    4.1.1设计代码

    • 基类     Object  拥有 整形  value,num ;
    • 派生类  base    拥有 整形  sum ,  fib;

    具体实现如下:

    1. class Object {
    2. public:
    3. int value;
    4. int num;
    5. public:
    6. Object(int x = 0, int y = 0) :value(x), num(y) {
    7. cout << "Create Object :" << this << endl;
    8. }
    9. };
    10. class Base :public Object {
    11. public:
    12. int sum;
    13. int fib;
    14. public:
    15. Base(int a = 0, int b = 0) :sum(a), fib(b) {
    16. cout << "Create Base :" << this << endl;
    17. }
    18. };
    19. int main() {
    20. Base base1;
    21. }

    4.1.2内存分配

    4.1.3能否理解为派生类继承后拥有基类的成员属性?

    如上图可以把Base派生类分为三部分,如下图:

     所以对于派生类来说有三个成员:隐藏基类对象、sum、flag成员。

    4.2公有继承、私有继承、保护继承(派生类类成员函数访问)

    4.2.1问题分析及代码设计

    探讨各种继承关系之间的派生类对基类成员的访问属性;可以设计如下代码 ;用派生类的fun函数去调用基类的成员;其中  ax是私有成员  ay 保护成员  az是公有成员

    类A如下所示:

    1. class A {
    2. private:
    3. int ax;
    4. protected:
    5. int ay;
    6. public:
    7. int az;
    8. public:
    9. A() { ax = ay = az = 0; }
    10. };

    类B如下所示:

    1. class B
    2. {
    3. private:
    4. int bx;
    5. protected:
    6. int by;
    7. public:
    8. int bz;
    9. public:
    10. B() { bx = by = bz = 1; }
    11. void fun() {
    12. ax = 10; ay = 20; az = 30;
    13. }
    14. };

    4.2.2 三种继承(类成员函数访问)

    公有继承:

    保护继承:

    私有继承:

    4.2.3总结:

    在继承中;不管那种继承方式私有成员在类内类外都不可访问;

    4.3三种继承(继承的对象和成员直接关系)

    4.3.1问题分析、代码设计

    假设 继承的派生类中含有基类的对象,例如如上派生类代码改为:

    1. class B :public A
    2. {
    3. private:
    4. int bx;
    5. A a;
    6. protected:
    7. int by;
    8. public:
    9. int bz;
    10. public:
    11. B() { bx = by = bz = 1; }
    12. void fun() {
    13. //ax = 10; ay = 20; az = 30;
    14. //a.ax = 15; a.ay = 25; a.az = 35;
    15. }
    16. };

    那么将A a定义在派生类的公有属性、保护属性、私有属性中有什么不同?

    4.3.2结果分析

    内存图:

     在私有属性中中定义;

     在保护属性中定义:

     在公有属性中:

     4.3.3总结

    在派生类中声明基类的具有名对象;无论该对象处于什么属性中;只能访问具有名对象的公有属性;

    4.4三种继承(外部函数和成员函数的区别)

    4.4.1问题分析、代码设计

    如果在派生类外的函数中访问派生类的属性会出现什么情况;例如在主函数(main)中给派生类对象赋值会出现什么结果?那种通过不了?为什么? 如果

    主函数如图所示:

    1. int main() {
    2. B b;
    3. b.bz - 100;
    4. b.az - 100;
    5. b.ay - 100;
    6. b.a.az - 100;
    7. b.a.ay - 100;
    8. return 0;
    9. }

    4.4.2结果分析

    情况一:A a在派生类的公有属性中:派生类公有继承基类

     情况二:派生类私有继承基类

     情况三:将A a定义在保护和私有中A a将都不可访问;

      

    4.4.3总结

    外部函数可以访问类中的公有成员,可以访问公有继承中的公有成员,可以访问公有成员对象中的公有成员。

    4.5三层继承关系

    4.5.1问题分析、代码设计

    三层继承其实和两层继承差不多;理解好其中的关系即可;现在设计三个类;

    类A:

    1. class A {
    2. private: int ax;
    3. protected: int ay;
    4. public: int az;
    5. public:
    6. A() { ax = ay = az = 0; }
    7. };

    类B:

    1. class B
    2. {
    3. private: int bx;
    4. protected: int by;
    5. public: int bz;
    6. public: B() { bx = by = bz = 1; }
    7. };

    类C:

    1. class C
    2. {
    3. private: int cx;
    4. protected: int cy;
    5. public: int cz;
    6. };

    假设我们改变其中的继承关系会怎样影响类C中的的访问权限?

    4.5.2结果分析

    情况一:B公有继承A;C公有继承B类;

    内存图:

     fun函数中都能访问;但是A类和B类的私有成员不能访问;

    情况二:将B类公有继承A类改为私有继承A类会怎样;

    情况三: 将B类私有继承A类改为保护继承A类会怎样;

     4.5.3总结

    在多层继承中;派生类能否访问基类的对象成员;在于基类继承的基类成员是什么属性;如果基类继承私有继承;那么在基类的成员属性就为基类的私有成员;而私有成员被继承下来是不可访问的;

    4.6继承中的同名隐藏

    4.6.1问题分析、代码设计;

    看如下代码的运行;思考运行结果ax=100是赋值给基类A还是派生类B;

    1. class A
    2. {
    3. protected:
    4. int ax;
    5. public:
    6. A() :ax(0) {}
    7. };
    8. class B :public A
    9. {
    10. private:
    11. int ax;
    12. public:
    13. B():ax(10){}
    14. void fun() {
    15. ax = 100;
    16. }
    17. };
    18. int main() {
    19. B b;
    20. b.fun();
    21. }

    内存图:

     那如果调用fun()函数了;我们都知道;会首先构建基类对象对象,那赋值是不是也先给基类对象赋值?

    4.6.2结果分析

     4.6.3总结

    如果派生类与基类有相同的函数名字或者对象名字;派生类会访问自己的对象(就近原则);或者理解为编译期时候派生类对象隐藏了基类同名对象;所谓同名隐藏;

    那怎么可以访问了?回答:假设类的作用域;

    4.7切片问题

    4.7.1问题分析、代码设计

    在继承中,派生类对象是否可以给基类对象赋值?

    1. class Object
    2. {
    3. private:
    4. int value;
    5. public:
    6. Object(int x = 10) :value(x) {}
    7. ~Object() {}
    8. };
    9. class Base :public Object {
    10. private:
    11. int num;
    12. public:
    13. Base(int x = 0) :num(x), Object(x + 10) {}
    14. ~Base() {};
    15. };

    如果main函数作如下处理会不会改变基类的对象值?

    1. Object obja(100);
    2. Base s1(10);

    4.7.2结果显示

     4.7.3总结

    派生类在初始化时,通过参数列表改变基类的值,会发生切片现象;但是必须在公有继承之下;

    4.7.4 问题一:那如果把上述代码中的参数列表初始化改为如下;会不会改变?

    1. class Base :public Object {
    2. private:
    3. int num;
    4. public:
    5. Base(int x = 0) :num(x)
    6. {
    7. Object(x + 10);
    8. }
    9. ~Base() {};
    10. };

    4.7.5思考:基类能否给派生类成员赋值?

    4.8拷贝构造、赋值函数是否具有继承性

    4.8拷贝构造函数分析

    代码如下;是想用base能base2赋值;能不能调动基类的拷贝构造?

    1. class Object
    2. {
    3. private:
    4. int value;
    5. public:
    6. Object(int x = 10) :value(x) { cout << "Create Object :" << this << endl; }
    7. ~Object() { cout << "Destroy Object :" << this << endl; }
    8. Object(const Object& obj) :value(obj.value) {
    9. cout << "Copy Create Object :" << this << endl;
    10. }
    11. };
    12. class Base :public Object {
    13. private:
    14. int num;
    15. public:
    16. Base(int x = 0) :num(x), Object(x + 10) { cout << "Create Base:" << this << endl; }
    17. ~Base() { cout << "Destroy Base: " << this << endl; }
    18. Base(const Base& base) :num(base.num) {
    19. cout << "Copy Creater Base :" << this << endl;
    20. }
    21. };
    22. int main() {
    23. Base base(10);
    24. Base bas2(base);
    25. return 0;
    26. }

    4.8.2代码分析

     

     所以并不会去调用基类的拷贝构造函数;也就是说拷贝构造函数不具有继承性;

    4.8.3怎样才能调用基类的拷贝构造?

    原因是赋值兼容规则;上文有讲述; 4.7


     

  • 相关阅读:
    这八种情形,专利优先审查一律不予推荐!
    【React + Ant Design】表单如何在前置项未填写时禁止后置项交互并提示
    RabbitMQ 保证消息可靠性
    浪漫七夕—很幸运一路有你
    面向对象基础
    【0147】当参数shared_memory_type分别为sysv和mmap时,差异为何如此大?
    谷粒学苑 —— 9、课程管理:课程列表
    九、Spring Boot 缓存(2)
    MVSNet depthfusion配置流程
    Arrays类中常用的方法
  • 原文地址:https://blog.csdn.net/weixin_48560325/article/details/125997820