• 波奇学C++:继承


    继承是为了复用代码,成员的变量或者成员函数

    1. class Person
    2. {
    3. public:
    4. protected:
    5. string _name="li";
    6. int _age=1;
    7. };
    8. class Student :public Person
    9. {
    10. public:
    11. void print()
    12. {
    13. cout << _age;
    14. }
    15. protected:
    16. int _stuid=2;
    17. };

    子类student公有继承基类Person,子类,继承Person所有成员变量,相当于copy一份,成员函数在公有,但子类可以访问。

    子类的访问受到继承方式和父类访问限定符的影响

    父类的public父类的protected父类的private
    public子类public子类的protected子类private
    protect子类protected子类protected子类private
    private不可见不可见不可见

    不可见指的是类外都不能访问,private相比,至少类中可以访问。

    实际上子类的访问=min(父类的访问限定符,继承方式)

    protected 用于存放可以私有且子类可以访问。

    不加继承方式,class默认私有继承,struct默认公有继承。

    父类不能直接赋值子类(向下转换),子类可以给父类(向上转换),这是一种赋值兼容,把子类中父类的部分切割赋值给父类,而不是强制类型转换

    1. student=person;//报错
    2. person=student;

    子类重定义/隐藏父类成员

    子类和父类同名成员变量,成员函数,编辑器优先选择子类,通过父类域可以选择父类的变量,函数,编译器优先顺序:作用域>类域>全局域。

    1. class Student:public Person
    2. ........
    3. void Print()
    4. {
    5. cout<< num;
    6. cout<< Person::num;
    7. }

    *注意不是重载关系,因为重载要在同一个作用域,而子类和父类分别为两个类域。

    派生类的构造,拷贝构造,赋值重载

    1. //基类
    2. class Person
    3. {
    4. public:
    5. Person(const char* name = "peter")
    6. : _name(name)
    7. {
    8. cout << "Person()" << endl;
    9. }
    10. Person(const Person& p)
    11. : _name(p._name)
    12. {
    13. cout << "Person(const Person& p)" << endl;
    14. }
    15. Person& operator=(const Person& p)
    16. {
    17. cout << "Person operator=(const Person& p)"<< endl;
    18. if (this != &p)
    19. {
    20. _name = p._name;
    21. }
    22. return *this;
    23. }
    24. ~Person()
    25. {
    26. cout << "~Person()" << endl;
    27. }
    28. protected:
    29. string _name;
    30. };
    1. //子类
    2. class Student :public Person
    3. {
    4. public:
    5. Student()
    6. :_id(0)
    7. {
    8. }
    9. protected:
    10. int _id;
    11. };

    子类Student继承了父类Person,继承相当于copy了一份父类的成员变量,因此我们理解子类中有_name和_id两个变量。但不能在初始化列表中初始化_name,但可以在函数内部中。

    初始化列表相当于在定义时初始化,可以理解为编译器在基类的定义找不到父类中的_name声明。

    实际上继承的父类的成员变量,在初始化列表,默认调用父类构造函数来初始化。

    1. // 子类构造函数
    2. Student()
    3. :_id(0)
    4. ,Person("xiaoli")
    5. {
    6. _name="xixiaong"
    7. }

    注意Person的初始化优先于,_id的初始化

    理解成父类成员变量的声明先于子类成员变量。

    派生类的拷贝构造

    1. Student(const Student& s)
    2. :Person(s)
    3. ,_id(s._id)
    4. {
    5. }

    用父类的拷贝构造,子类作为参数,拷贝构造父类的成员变量,我们此前所说的向上转换,子类给父类是指出的,再拷贝构造基类自身成员变量。

    如果不显示调用拷贝构造,会调用默认构造。

    派生类的赋值重载,除了子类向上构造,还有注意operator=()重定义的问题。

    1. Student& operator=(const Student& s)
    2. {
    3. if (this != &s)
    4. {
    5. Person::operator=(s);
    6. _id = s._id;
    7. }
    8. return *this;
    9. }

    析构函数不用显式调用父类析构函数

    析构函数名被特殊处理destructor,在子类析构调用后,会自动调用父类析构函数。来保证先子后父

    1. ~Student()
    2. {
    3. }

    先子后父的原因:初始化列表时,先调用父类构造函数,所以结束时后调用父类析构函数。子类可能用到父类的函数,成员变量,如果父类被析构了,那么就会报错。

    友元关系不能继承。

    静态成员变量属于子父类公有,子父类的静态成员变量是共有的,而不是复制的关系

    1. class Person
    2. ......
    3. protected:
    4. string _name;
    5. static int _count;
    6. };
    7. int Person::_count = 0;

    多继承和菱形继承

    Assiatant类继承了Student和Teacher两个类

    1. class Assistant :public Student, public Teacher
    2. {
    3. protected:
    4. string _majorCourse;
    5. };

     菱形继承存在的问题,student可能和teacher同时从Person那里继承了age,使得assistant类中有两个年龄造成数据冗余和二义性

     虚继承解决冗余,二义性问题

    1. // Student和Teacheer类虚继承了Person
    2. class Student :virtual public Person
    3. {
    4. protected:
    5. int _num;
    6. };
    7. class Teacher :virtual public Person
    8. {
    9. protected:
    10. int _id;
    11. };

    二义性的证明

    1. class A
    2. {
    3. public:
    4. int _a;
    5. };
    6. class B : public A
    7. {
    8. public: int _b;
    9. };
    10. class C : public A
    11. {
    12. public: int _c;
    13. };
    14. class D : public C, public B
    15. {
    16. public: int _d;
    17. };

    由图可得_a有两个分别在B,C中。并且类A,B,C的变量依次排列

    虚继承virtual

     _a 的位置存放着一串地址

    对应地址的下个位置的数值为20,12,恰好就是B,C到A的偏移量。

    本质上保存的是_a的相对位置,那么为什么不直接保存A的地址,或者偏移量而是要保存指向一块空间的地址?如果存在多个D对象,因为相对位置是固定的,所以可以直接复用。d2 和d1的绝对地址是不一样的,但是相对位置是一样的。

    右边的表又叫为虚基表,专门保存偏移量。

    总结:把它单独划分出来便于复用。

    虚继承后B,C,D 都报错相似的结构,有虚基表

     偏移量的运用

    当ptr无论指向B 还是C 都可以通过偏移量,找到A

  • 相关阅读:
    cs11 Python flask Python
    vs2019+Qt实现打开影像并用鼠标滚轮控制图片缩放
    针对BSV区块链新推出的网络访问规则NAR和警报系统AS的解释与问答
    事实 读书笔记
    sql注入漏洞(CVE-2022-32991)
    macOS下matplotlib如何显示中文字体?
    【论文翻译】FCOS: Fully Convolutional One-Stage Object Detection
    直线模组在点胶机中发挥着怎样的作用?
    k8s helm Seata1.5.1
    SAP UI5 的规则构建器控件介绍
  • 原文地址:https://blog.csdn.net/Bagayalu1234567/article/details/132707804