• c++之旅第七弹——继承


    大家好啊,这里是c++之旅第七弹,跟随我的步伐来开始这一篇的学习吧!

    如果有知识性错误,欢迎各位指正!!一起加油!!

    创作不易,希望大家多多支持哦!

    一.继承和派生:(实质上是一个过程,不过是从不同角度来看的)

    1.继承方式:

    单继承:

    1. class A
    2. {};
    3. class B:public A
    4. {};

    多继承:

    1. class D
    2. {};
    3. class E
    4. {};
    5. class F:public D,public E
    6. {};

    多级继承:

    1. class A
    2. {};
    3. class B:public A
    4. {};
    5. class C:public B
    6. {};
    7. class D:public C
    8. {};
    9. //多级继承,
    10. //D 拥有了 A,B,C 的所有属性

    2.继承涉及的属性问题:

    (1)以上的继承动作属性都是公有,不过还有和类中成员相同的其他属性:私有和保护

    继承时基类中的成员属性和继承动作的属性是一起决定派生类中的成员属性的,两者同时考虑,那个更严格派生类中该成员的属性就是哪个属性(私有>保护>公有),所以一般使用公有继承,这样可以保留原来基类中成员的原有属性不变

    (2)如果基类中某一成员是私有属性,那么无论使用那种继承方式,在派生类中也是无法进行直接访问的,要访问也只能通过公有接口来进行访问,如果类B继承类A时继承方式是私有,而基类A中成员的属性为公有,那么在派生类B中是可以进行基类A中该成员的访问的,而当派生类C继承B时使用任何继承方式都无法对基类A中的该成员进行访问,这是因为B继承A时使用私有继承方式,相当于A类是B类的私有属性成员。

    (3)保护属性成员可以在派生类中进行访问,但是不可以在派生类的派生类中进行访问

    (4)基类如果是带参构造,在派生类通过成员初始化列表进行构造函数的调用:

    1. class B :public A
    2. {
    3.    int d;
    4. public:
    5. B(int i) :A(i)//基类如果是带参构造,在派生类通过成员初始化列表进行构造函数的调用
    6. {
    7. d = 3;
    8. }
    9. ~B()
    10. {
    11. }
    12. };

    3.有关继承和派生中的内存问题:

    (1)派生类可以作为基类对象,基类对象不可以作为派生类对象,也就是进行赋值操作,这是因为派生类的内存是大于基类的内存的,用派生类对象来给基类对象进行赋值可以使基类对象中的内存都放满,剩余的不属于基类,放不进去也无所谓;而如果将基类赋值给派生类对象时,就会出现派生类对象的内存有空缺,无法表示派生类,所以这种赋值是无法进行的。

    (2)若使用多级继承,那么最终的类类型对象的成员在内存中的表现为嵌套时最内部的类的成员是储存在最终的派生类对象中最前面的内存区域

    三,继承之后:派生类可以继承父类中的所有数据成员和函数成员除了构造和析构函数,这是属于类自身的,无法进行继承

    (1)继承之后派生类的构成:继承时如果基类和派生类中有同名的成员,在派生类中进行调用时优先调用派生类中的成员,局部优先于全局,如果想要调用的是基类中的成员,那么需要使用类名作用域来进行调用,因为包含一个无名的对象,无法使用对象名作用域来进行访问,使用类名作用域代替之,多继承同样可以使用类名作用域来进行成员的访问

    1. //继承时的访问:
    2. class CA
    3. {
    4. protected:
    5. int a;
    6. public:
    7. CA()
    8. {
    9. a = 10;
    10. }
    11. };
    12. class CB : public CA
    13. {
    14. int a;
    15. public:
    16. CB()
    17. {
    18. CA::a = 123;//在CB对象里面包含一个无名的CA对象,所以只能通过类型去作用域访问
    19. a = 20;
    20. }
    21. };

    (2)在定义一个派生类对象时,其构造顺序为先构造基类对象,然后再在基类的基础上构造派生类对象,也就是调用构造函数的顺序,然后当程序运行结束时(假设是在栈区的对象)析构函数是反向来进行析构的,这和栈结构有关,但是在堆区进行以上操作时,也是遵循以上规律(构造是通过new开辟堆区内存,析构是手动使用delete来进行指针的删除)

    四, 菱形继承:一个基类(共同基类)派生出两个类(间接基类),这两个类再进行多继承至一个类(最终类)

    1.菱形继承虚继承时两个间接基类都要写virtual关键字,这样才能起到虚继承应该起到的作用,否则还是会进行共同基类的二次拷贝,因为写了virtual关键字才可以使间接基类拥有多的四个字节,有指针的作用,可以寻找共同基类是否已经构造过,从而避免多次拷贝

    2.虚继承间接基类中多的四字节相当于是指针,可以用来寻找共同基类是否已经构造过了,若构造过就不会再次构造,这样可以减少当共同基类内存大时的没有必要的二次拷贝内存

    3.尽量不写菱形继承,因为会引起一些问题,主要包括以下几点原因:

    (1)虚基类冗余数据:在菱形继承中,由于共同基类被多次继承,会导致虚基类中的数据在派生类中存在多份冗余拷贝,增加了内存消耗。

    (2)构造函数调用顺序不明确:在菱形继承中,由于存在多条继承路径,构造函数的调用顺序可能变得复杂,不易理解和维护。

    (3)内存布局不清晰:由于存在多份冗余数据和多条继承路径,菱形继承的内存布局变得复杂,容易引起内存对齐、访问越界等问题。

    (4)代码可读性差:菱形继承会增加代码的复杂性,降低代码的可读性和可维护性,使程序难以理解和调试。

    如果一定要写,尽量使用虚继承方式

    4.构造顺序:虚基类->基类->派生类,虚基类的无名对象是在继承的最终类对象中的最后部分的内存,且只有一份它的内存。

  • 相关阅读:
    电脑上播放4K视频需要具备哪些条件?
    Ceph(L版本)部署及相关概念
    全面了解SpringBoot拦截器
    APEX数据源加载实现Excel表数据导入及自定义存储过程
    关于笔试编程题被坑的输入问题,acm模式下的python输入究竟如何写?
    前端技术社区总目录
    uniapp中使用axios打包到小程序时报 TypeError: adapter is not a function
    spdk intel
    数据库系统[备考]
    即时通讯如何做好安全防御
  • 原文地址:https://blog.csdn.net/2301_80101699/article/details/136778694