【C++ 进阶】继承

简介: 【C++ 进阶】继承

一.继承的定义格式

基类又叫父类,派生类又叫子类;


二.继承方式

继承方式分为三种:

    1.public继承

    2.protected继承

    3.private继承

基类成员与继承方式的关系共有9种,见下表:

虽然说是有9种,但其实最常用的还是红框里的,其它的很少用。

总结

1. 基类private成员在派生类中无论以什么方式继承都是不可见的

   不可见指:基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不       管在类里面还是类外面都不能去访问它;

2.如果想要在派生类访问基类中的成员变量,可以在基类中定义为 protected 成员

3.基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式)

4.使用关键字class时默认的继承方式是private

  使用struct时默认的继承方式是public


三.派生类和基类之间的赋值转换

1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用,这个过程又被形象的称为      切片/切割

2.基类对象不能赋值给派生类对象

3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的;


四.隐藏(重定义)

1.子类和父类中,只要函数名相同就构成隐藏

2.成员名相同也构成隐藏;

3.在子类成员函数中,可以使用 基类::基类成员 显示访问隐藏的函数或成员;

4.最好不要定义同名的成员变量。

隐藏(重定义)与重载

重载:在同一作用域

隐藏:在不同的作用域,一个在父类,一个在子类


五.派生类中的默认成员函数

1.构造函数

  派生类必须先自动调用基类的默认构造(初始化基类的那一部分成员),如果基类没有默     认构造,就要在派生类的初始化列表阶段显式调用基类的构造函数,然后派生类调用自己     的构造函数。

2.拷贝构造

  派生类的拷贝构造必须先调用基类的拷贝构造完成基类的拷贝初始化。

3.赋值重载

  派生类赋值重载必须先调用基类的赋值重载完成基类的复制。

4.析构函数

  销毁对象时,会先调用派生类的析构函数,然后再自动调用基类的析构函数,这样就保证     了析构的顺序(即先子后父)

  如果不是这个顺序,一个成员可能会析构两次,就会导致程序崩溃,比如以下的代码:

1. class A
2. {
3. public:
4.  ~A()
5.  {
6.    delete _a;
7.  }
8. 
9.  int* _a = new int;
10. };
11. 
12. class B :public A
13. {
14. public:
15.   ~B()
16.   {
17.     delete _b;  
18.     A::~A();   //显式调用父类的析构函数,在调用完后,编译器又会自动调用一次父类的析构函数, 
19. //导致父类成员_a析构了两次,从而程序崩溃
20.   }
21. 
22.   int* _b = new int;
23. };
24. 
25. int main()
26. {
27.   A  a;
28.   B b;
29.   return 0;
30. }

六.继承与友元,静态成员

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员;

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子

类,都只有一个static成员实例 ,静态成员不属于任何一个具体的实例对象,而是属于类本身,子类可以继承父类的静态成员,而不需要重新定义。


七.多继承

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

下面就是一个多继承:

1. class A
2. {};
3. 
4. class B
5. {};
6. 
7. class C:public A,public B   //多个父类之间用逗号隔开
8. {};

多继承的一个坑:菱形继承

上图就构成了一个菱形继承,它会导致两个问题:

  1.数据冗余,因为会存两个Person类;

  2.二义性

1. class A
2. {
3. public:
4.  int _a;
5. };
6. 
7. class B :public A
8. {
9. public:
10.   int _b;
11. };
12. 
13. class C :public A
14. {
15. public:
16.   int _c;
17. };
18. 
19. class D :public B, public C
20. {
21. public:
22.   int _d;
23. };
24. 
25. int main()
26. {
27.   D d;
28.   d._a;   //二义性问题,不知道访问的是哪个_a;
29.     d.A::_a;   //显式访问,解决了二义性问题,但无法解决数据冗余问题
30. 
31.   return 0;
32. }

虚拟继承:解决菱形继承问题

在菱形继承的腰部加上 virtual  关键字构成虚拟继承

如将上面代码class B 和  class C  改成:

1. class B: virtual public A   //虚拟继承
2. {};
3. 
4. class C: virtual public A
5. {};

原理

通过观察菱形继承和虚拟继承的内存窗口我们发现:

 


八.总结和反思

1.public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象

2.组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象

例如:

1. class A
2. {};
3. 
4. class B
5. {
6.     A a;   //组合
7. };

3.组合的耦合性比继承低,所以一般推荐使用组合。


🐬🤖本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;🕊️👻

😄😆希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;🥰🤩

😍😁谢谢你的阅读。😸😼

目录
相关文章
|
6天前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
37 16
|
2天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
20 5
|
2月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
38 1
【C++】继承
|
6月前
|
编译器 C++
【C++】详解C++的继承
【C++】详解C++的继承
|
3月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
99 11
|
3月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
69 1
|
3月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
53 1
|
3月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
25 0
|
3月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
45 0
|
3月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
51 0