【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.组合的耦合性比继承低,所以一般推荐使用组合。


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

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

😍😁谢谢你的阅读。😸😼

目录
相关文章
|
5天前
|
安全 前端开发 Java
【C++】从零开始认识继承二)
在我们日常的编程中,继承的应用场景有很多。它可以帮助我们节省大量的时间和精力,避免重复造轮子的尴尬。同时,它也让我们的代码更加模块化,易于维护和扩展。可以说,继承技术是C++的灵魂。
14 1
|
5天前
|
安全 程序员 编译器
【C++】从零开始认识继承(一)
在我们日常的编程中,继承的应用场景有很多。它可以帮助我们节省大量的时间和精力,避免重复造轮子的尴尬。同时,它也让我们的代码更加模块化,易于维护和扩展。可以说,继承技术是C++的灵魂。
24 3
【C++】从零开始认识继承(一)
|
5天前
|
存储 编译器 C++
C++中的继承
C++中的继承
11 0
|
5天前
|
设计模式 算法 编译器
【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]
【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]
13 0
|
5天前
|
安全 程序员 编译器
【C++】继承(定义、菱形继承、虚拟继承)
【C++】继承(定义、菱形继承、虚拟继承)
15 1
|
5天前
|
安全 编译器 程序员
[C++基础]-继承
[C++基础]-继承
|
5天前
|
C++ 芯片
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
|
5天前
|
编译器 C++
【C++进阶】引用 & 函数提高
【C++进阶】引用 & 函数提高
|
5天前
|
安全 Java 程序员
【C++笔记】从零开始认识继承
在编程中,继承是C++的核心特性,它允许类复用和扩展已有功能。继承自一个基类的派生类可以拥有基类的属性和方法,同时添加自己的特性。继承的起源是为了解决代码重复,提高模块化和可维护性。继承关系中的类形成层次结构,基类定义共性,派生类则根据需求添加特有功能。在继承时,需要注意成员函数的隐藏、作用域以及默认成员函数(的处理。此外,继承不支持友元关系的继承,静态成员在整个继承体系中是唯一的。虽然多继承和菱形继承可以提供复杂的设计,但它们可能导致二义性、数据冗余和性能问题,因此在实际编程中应谨慎使用。
18 1
【C++笔记】从零开始认识继承
|
5天前
|
设计模式 编译器 数据安全/隐私保护
C++ 多级继承与多重继承:代码组织与灵活性的平衡
C++的多级和多重继承允许类从多个基类继承,促进代码重用和组织。优点包括代码效率和灵活性,但复杂性、菱形继承问题(导致命名冲突和歧义)以及对基类修改的脆弱性是潜在缺点。建议使用接口继承或组合来避免菱形继承。访问控制规则遵循公有、私有和受保护继承的原则。在使用这些继承形式时,需谨慎权衡优缺点。
25 1