13.C++:继承

目录

一、继承的概念及定义

1.1、继承的概念

1.2、继承的定义

1.2.1、语法形式

1.2.2、继承基类成员访问方式的变化

1.3、继承类模版

二、基类和派生类之间的转换

三、继承中的作用域

3.1、隐藏规则

3.2、练习题

四、派生类的默认成员函数

五、友元与继承

[六、静态成员与 继承](#六、静态成员与 继承)

七、多继承和菱形继承的问题

7.1、多继承

7.2、菱形继承

7.3、虚继承

八、继承和组合

Note:如何让一个类不能被继承?


在现实中,我们想拥有财富,无非是靠自己奋斗,或从父辈那里继承。后者显然是一条更轻松的捷径。在C++的世界里,面向对象编程为我们提供了类似的"捷径"------继承。它让一个类能够直接从另一个类获取已有的属性和方法,无需一切从零开始,这避免了"重复发明轮子",真正实现了"站在巨人的肩膀上"进行开发。本篇博客将为你详细解读C++的继承机制。

一、继承的概念及定义

1.1、继承的概念

继承机制是面向对象程序设计使代码可以重复使用的一种手段。继承是类设计层次的复用。

比如:学校管理系统。学校里面有学生,老师,职工,这三类有个共同特点:都是人。所以记录它们信息的时候有一部分是必然重叠的(姓名,年龄,性别等)那么我们可以专门创建一个 person 类,然后再创建student ,teacher,worker 三个类,让这三个类去继承 person 这个类就好了。

对于被继承的类,我们称之为 基类(也叫父类),继承别的类的类,称为 派生类(也叫子类)

1.2、继承的定义

1.2.1、语法形式

在派生类名后加个 : ,然后接着继承方式,基类就可以了。

继承方式有三种:

1.2.2、继承基类成员访问方式的变化

|----------------|-----------------|-----------------|---------------|
| | public继承 | protected继承 | private继承 |
| 基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
| 基类的protected成员 | 派生类的protected成员 | 派生类的protected成员 | 派生类的private成员 |
| 基类的private成员 | 派生类不可见 | 派生类不可见 | 派生类不可见 |

Note:基类的私有成员,在派生类中不可见,语法上限制不让用。对于如此庞杂的局面,我们只需要记住 public > protected > private 就可以了。基本上,我们使用的都是公有继承。

Note:使用关键字 class,默认继承方式为 private,使用 struct 默认继承方式为 public,不过最好还是显式写出继承方式

1.3、继承类模版

模版是按需实例化,调用到哪个成员函数,就实例化哪个成员函数,所以必须要指定!

说人话就是,在派生类中调用基类成员时,需要在前面用 :: 指定是哪个类型的

二、基类和派生类之间的转换

大家都知道,在普通对象之间的类型转换时,会产生一个临时的中间变量,而该临时变量具有常性。但是,基类和派生类不是这样的,派生类给基类的引用或指针赋值,不会产生临时变量

如图,派生类给基类的引用或指针赋值,该引用或指针会直接截取本来自己有的那部分,不会产生临时变量。但是,只能是派生类赋值给基类的引用和指针,基类不能给派生类。(子可以给父,父不能给子)

三、继承中的作用域

3.1、隐藏规则

(1)基类和派生类都有独立的作用域

(2)如果基类和派生类中有同名成员,派生类会直接屏蔽掉对基类的成员访问

(3)如果是同名函数,只要函数名字相同,就隐藏(不管参数和返回类型),也就是说,没有重载这个说法了

(4)最好不要定义同名成员

3.2、练习题

(1)下面 A 和 B 类中的两个 func 构成什么关系? B

A.重载 B.隐藏 C.没关系

(2)下面程序的编译运行结果是什么? A

A.b编译报错 B.运行报错 C.正常运行

首先,A 是基类,B是派生类,由于两个成员函数的函数名是相同的,所以构成了隐藏关系,在派生类对象中调用 func 会直接隐藏基类的 func ,所以 版本 b.func() 不传任何参数就会报错。

四、派生类的默认成员函数

派生类的成员变量 = 自己的成员变量 + 基类的成员变量

自己的成员变量就跟普通类的成员变量一样,该咋滴就咋地。对于基类的成员变量,在调用派生类构造时,它会自动调用基类的默认构造(或者你手动调用)

1.派生类的构造函数,必须调用基类的构造函数,初始化基类的那部分成员。如果基类没有默认构造,那就必须在派生类构造函数的初始化时显式调用

2.派生类的拷贝构造函数必须调用基类的拷贝构造

3.派生类的赋值符重载(operator=)必须要调用基类的赋值符重载(operator=)

4.派生类的析构函数会在被调用完后自动调用基类的析构函数,因为这样才能保证派生类对象清理完再清理基类对象的顺序

5.派生类初始化,先调用的是基类的构造函数

6.派生类对象析构,先清理派生类,再清理基类

7.因为多态中一些场景的析构函数需要构成重写,重写的条件之一是函数名先攻,所以编译器会对析构函数的名字进行特殊处理,为 destructor() ,所以基类和派生类的析构函数会构成隐藏关系

五、友元与继承

友元关系不能继承

六、静态成员与 继承

基类定义了 static 静态成员,则整个继承体系里,都只有一个这样的成员。也就是说,不管派生出都少个派生类,都只有这一个 static 成员

七、多继承和菱形继承的问题

7.1、多继承

单继承:一个派生类只有一个直接基类时,称为单继承

多继承:有两个或两个以上的直接基类

Note:在多继承中,先继承的基类成员放在前面,后继承的基类成员放在后面,最后才是派生类自己的成员

7.2、菱形继承

比如学校管理系统的研究生,他即是学生,又是助教,所以他会继承学生和助教两个基类。但是,学生和助教都是人,学生会继承人,助教也会继承人,这样会导致最后研究生里面有两个人的信息,造成数据的冗余

7.3、虚继承

如何解决菱形继承的问题呢?像 Java 直接不支持多继承,避免了菱形继承,而 C++则是提出了一个办法,虚继承。

虚继承的关键字为: virtual

cpp 复制代码
class Person {
protected:
    string name;
public:
    Person(string n) : name(n) {}
    void introduce() {
        cout << "我是" << name << endl;
    }
};

// 虚继承
class Student : virtual public Person {
public:
    Student(string n) : Person(n) {}
    void study() {
        cout << name << "在学习" << endl;
    }
};

// 虚继承
class Teacher : virtual public Person {
public:
    Teacher(string n) : Person(n) {}
    void teach() {
        cout << name << "在授课" << endl;
    }
};

// 研究生既是学生又是老师
class GraduateStudent : public Student, public Teacher {
public:
    GraduateStudent(string n) : Person(n), Student(n), Teacher(n) {}
    
    void doResearch() {
        cout << name << "在做科研" << endl;  // 直接访问,没有二义性
    }
};

Note:谁的数据会造成冗余和二义性就给谁用,这里肯定是给人用,而不是给老师和学生用

八、继承和组合

继承和组合本质上都是对类的复用。

继承是一种 is-a 的关系 ,而组合是 has -a 的关系

Note:建议优先使用组合,而不是继承,组合的代码耦合性低,更好维护

Note:如何让一个类不能被继承?

(1)将构造函数放到私有。

(2)使用 final 修饰

cpp 复制代码
class Base final
{
    //......
};

恭喜你已经阅读完本篇博客的全部内容,如果有不足和错误,恳请批评和指正,期待我们的下次相会!

相关推荐
张人玉4 小时前
c#串口读写威盟士五插针
开发语言·c#·通讯
路长冬4 小时前
matlab与数字信号处理的不定期更新
开发语言·matlab·信号处理
卡卡酷卡BUG4 小时前
Java 后端面试干货:四大核心模块高频考点深度解析
java·开发语言·面试
Yolo566Q4 小时前
OpenLCA生命周期评估模型构建与分析
java·开发语言·人工智能
凡同学。4 小时前
通信人C++自学
c++·应届生秋招·后端四件套
安娜的信息安全说5 小时前
深入浅出 MQTT:轻量级消息协议在物联网中的应用与实践
开发语言·物联网·mqtt
威桑5 小时前
C++ Linux 环境下内存泄露检测方式
linux·c++
在坚持一下我可没意见5 小时前
HTTP 协议基本格式与 Fiddler 抓包工具实战指南
java·开发语言·网络协议·tcp/ip·http·java-ee·fiddler
樱花开了几轉5 小时前
element ui下拉框踩坑
开发语言·javascript·ui