• C++基础知识(六:继承)


    首先我们应该知道C++的三大特性就是封装、继承和多态。

    此篇文章将详细的讲解继承的作用和使用方法。

    • 继承

    一个类,继承另一个已有的类,创建的过程

    父类(基类)派生出子类(派生类)的过程

    继承提高了代码的复用性

    【1】继承的格式

    1. class 类名:父类名
    2. {};

    【2】继承的权限

    1. class 类名:继承的权限 父类名
    2. {};
    3. 如果不写继承方式,默认是私有继承
    1. 父类中的权限      public|private|protected  public|private|protected   public|private|protected              
    2. 继承方式                 public                    private                   protected
    3. 子类中的权限      public|不能访问|protected  private|不能访问|private    protected|不能访问|protected

    【3】继承时类中的特殊成员函数

    特殊的成员函数不会被继承

    构造函数:

    • 需要在子类中显性调用父类的构造函数(初始化列表中)(透传构造)
    • 透传构造
    • 继承构造
    • 委托构造

    需要在子类中显性调用父类构造函数的场合:

    父类中只有有参构造  ----->子类在创建类对象时,必须手动调用父类的构造函数

    1. #include <iostream>
    2. using namespace std;
    3. class Father
    4. {
    5. public:
    6. int age;
    7. char c;
    8. Father(int a,char c):age(a),c(c){cout << "Father有参" << endl;}
    9. };
    10. class Child:public Father    //----->私有继承
    11. {
    12. int high;
    13. public:
    14. void show()
    15. {
    16.         cout << c << endl;
    17. }
    18. };
    19. int main()
    20. {
    21. //Child c1;  //error,因为父类只有有参构造,而子类中没有提供能够调用父类有参构造的构造函数,不能成功创建父类的空间
    22. //Father f1;
    23.     c1.age;
    24.     cout << sizeof (Father) << endl;
    25.     cout << sizeof (Child) << endl;
    26. return 0;
    27. }

    i)透传构造

    在子类构造函数的初始化列表中,显性调用父类的构造函数

    ii)继承构造

    C++支持

    不用在子类中再写一遍父类的构造函数

    使用:using Father::Father; ----->在子类中使用父类的构造函数

    直接使用继承构造的方式,不能对子类成员初始化

    继承构造本质上并不是把父类中的构造函数继承给子类,编译器自动根据父类中构造函数的格式,提供出派生的构造函数(个数和参数都和父类中的构造函数一致),主要还是通过透传构造创建父类的空间

    1. #include <iostream>
    2. using namespace std;
    3. class Father
    4. {
    5. public:
    6. int age;
    7. char c;
    8. //    Father(){cout << "Father无参" << endl;}
    9. Father(int a,char c):age(a),c(c){cout << "Father有参两个参数" << endl;}
    10. Father(char c):c(c){cout << "Father有参一个参数的" << endl;}
    11. Father(Father &other):age(other.age),c(other.c)
    12. {cout << "Father拷贝" << endl;}
    13. };
    14. class Child:public Father    //----->私有继承
    15. {
    16. private:
    17. int high;
    18. //父类中的public成员age,通过公有继承,仍然是public
    19. using Father::age; //把父类中公有继承下来的age成员,在子类中改成私有权限
    20. public:
    21. void show()
    22. {
    23.         cout << c << endl;
    24. }
    25. //子类的无参构造,但是显性调用父类的有参构造还给了默认值
    26. //透传构造
    27. //    Child():Father(12,'a'){cout << "Child无参构造" << endl;}
    28. //    Child(int a,char c,int h):Father(a,c),high(h)
    29. //    {cout << "Child有参构造" << endl;}
    30. //父类中的所有构造函数,都被继承到了子类中
    31. using Father::Father; //更高效一些
    32. };
    33. int main()
    34. {
    35.     Child c1(10);
    36.     Child c2(20,'z');
    37.     Child c3 = c2;
    38. //Father f1;
    39. //c1.age;
    40.     cout << sizeof (Father) << endl;
    41.     cout << sizeof (Child) << endl;
    42. return 0;
    43. }

    iii)委托构造

    一个类的情况,并不直接通过无参构造实例化对象,而是无参构造委托了有参构造,实例化对象

    继承时的情况

    1.     Child():Child(10){cout << "Child无参构造" << endl;}   //Child c1
    2.     Child(int a):Father(12,'a'),high(a)
    3.     {cout << "Child有参构造一个参数" << endl;}

    iv)拷贝构造

    需要在初始化列表中显性调用父类的拷贝构造,传other对象到父类的拷贝构造中

    1. Father(Father &other):age(other.age),c(other.c){cout << "Father的拷贝构造" << endl;}
    2. Child(Child &other):Father(other),high(other.high){}

    【4】继承时构造和析构的时机

    继承关系,可以理解为包含关系

    子类在继承父类时,会把父类中的成员保留一份,再来创建子类自己的成员

    父类先构造,子类后构造

    子类先析构,父类后析构

    1. #include <iostream>
    2. using namespace std;
    3. class F
    4. {
    5. int *p;
    6. public:
    7. F():p(new int){cout << "F无参构造" << endl;}
    8. ~F()
    9. {
    10. delete p;
    11.         cout << "F析构函数" << endl;
    12. }
    13. };
    14. class C:public F
    15. {
    16. int *p;
    17. public:
    18. C():p(new int){cout << "C无参构造" << endl;}
    19. ~C()
    20. {
    21. delete p;
    22.         cout << "C析构函数" << endl;
    23. }
    24. };
    25. int main()
    26. {
    27.     C *p1 = new C;
    28. delete p1; //空间释放时,会自动调用析构函数,无需手动调用
    29.     p1 = nullptr;
    30. return 0;
    31. }

    【5】父子类中存在同名成员问题

    访问时不会发生冲突,默认访问子类的

    1. #include <iostream>
    2. using namespace std;
    3. class F
    4. {
    5. public:
    6. int *p;
    7. F():p(new int){cout << "F无参构造" << endl;}
    8. ~F()
    9. {
    10. delete p;
    11.         cout << "F析构函数" << endl;
    12. }
    13. };
    14. class C:public F
    15. {
    16. public:
    17. int *p;
    18. C():p(new int){cout << "C无参构造" << endl;}
    19. ~C()
    20. {
    21. delete p;
    22.         cout << "C析构函数" << endl;
    23. }
    24. };
    25. int main()
    26. {
    27.     C *p1 = new C;
    28. *(p1->p) = 90;
    29.     cout << *(p1->p) << endl; //子类成员和父类成员同名,默认优先访问子类成员
    30.     cout << *(p1->F::p) << endl; //通过域限定符访问父类的成员
    31. delete p1; //空间释放时,会自动调用析构函数,无需手动调用
    32.     p1 = nullptr;
    33. return 0;
    34. }
    • 多重继承

    一个子类,继承自多个基类

    【1】格式

    1. class 类名:继承权限 父类名,继承权限 父类名····
    2. {}

    【2】当多个父类中包含同名成员

    多个父类中包含同名成员,通过域限定符访问指定的父类中成员

    1. #include <iostream>
    2. using namespace std;
    3. class Room
    4. {
    5. public:
    6. void clean()
    7. {
    8.         cout << "打扫房间" << endl;
    9. }
    10. };
    11. class BedRoom
    12. {
    13. public:
    14. void play()
    15. {
    16.         cout << "可以玩游戏" << endl;
    17. }
    18. void clean()
    19. {
    20.         cout << "打扫卧室" << endl;
    21. }
    22. };
    23. //Home类公共继承字Room和BedRoom类
    24. class Home:public Room,public BedRoom
    25. {
    26. };
    27. int main()
    28. {
    29.     Home h1;
    30.     h1.play();
    31.     h1.Room::clean();
    32.     h1.BedRoom::clean();
    33. return 0;
    34. }
    • 菱形继承(钻石继承)

    【1】格式

    1.      A                ----->公共基类
    2. /   \
    3.   B     C             ------>中间子类
    4.    \   /
    5.      D                ------>汇集子类

    汇集子类中,会包含两份公共基类中的内容

    【2】菱形继承存在的问题

    1. 会发生二义性的问题(同一个变量或者函数,可以通过两种方法访问)
    2. 如果公共基类,过大,会造成汇集子类中的代码膨胀/冗余
    1. #include <iostream>
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. int a;
    7. //A(int a):a(a){cout << "A" << endl;}
    8. };
    9. class B:public A
    10. {
    11. public:
    12. int c;
    13. //B(int a,int c):A(a),c(c){cout << "B" << endl;}
    14. };
    15. class C:public A
    16. {
    17. public:
    18. //C(int a):A(a){cout << "C" << endl;}
    19. };
    20. class D:public C,public B
    21. {
    22. public:
    23. //D(int a,int c):B(a,c),C(a),A(a){cout << "D" << endl;}
    24. };
    25. int main()
    26. {
    27.     D d1;
    28.     d1.B::= 90; //二义性,还可以直接通过中间子类访问,直接访问B中的a成员
    29. //cout << d1.C::A::<< endl;  //会发生二义性,因为访问A,但是有两条路径都访问到A
    30. return 0;
    31. }

    【3】虚继承(virtual)

    虚继承指对公共基类的虚继承。

    主要用于解决菱形继承问题,

    采用虚继承后,公共基类中的内容,只会在汇集子类中存在一份,在汇集子类中,可以通过任意一条路径访问到公共基类中的成员

    1. #include 
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. int a;
    7. };
    8. class B:virtual public A
    9. {
    10. public:
    11. int c;
    12. };
    13. class C:virtual public A
    14. {
    15. };
    16. class D:public B,public C
    17. {
    18. };
    19. int main()
    20. {
    21.     D d1;
    22.     d1.B::A::a = 90;
    23.     cout << d1.C::A::a << endl;
    24. return 0;
    25. }

  • 相关阅读:
    80端口和443端口是什么?有什么区别?
    ARM pwn 入门 (3)
    tomcat安装,创建web后端项目,部署项目过程
    【RocketMQ】主从同步实现原理
    IDEA中的常用设置
    网卡mac地址的设置
    php生成器
    基于核心素养劳动教育与学科教学融合研究结题报告
    LINUX基础 第五次课 10月22日
    解决nexus3登录x509: certificate has expired or is not yet valid
  • 原文地址:https://blog.csdn.net/LKHzzzzz/article/details/136269025