C++继承(上)

文章详细介绍了面向对象编程中的继承概念,包括继承的定义、基类与派生类的关系、访问限定符的影响、派生类的默认成员函数如构造、拷贝构造、赋值和析构函数的工作原理。此外,还讨论了如何设计防止继承的类,即通过将构造函数设为私有来阻止其他类的继承。
摘要由CSDN通过智能技术生成

我们知道,面向对象有三大特性:封装,继承,多态。封装前面已经说过,这篇文章主要说说继承。
在这里插入图片描述

1.继承的概念及定义

1.1继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继
承是类设计层次的复用

举个例子:
比如说我们学校有个管理系统,管理老师和学生。老师和学生中都会记录他们的名字,电话,年龄等等,但是他们的学校id是不一样的,一个是学号,一个是工号。那么有些数据和方法每个角色都有的,就设计重复了。此时继承就是类设计层次的复用,那么如何使用,我们来看:
在这里插入图片描述
继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
Student和Teacher复用了Person的成员。
在这里插入图片描述
调用Print可以看到成员函数的复用。那么继承的语法格式是什么样的,我们来看。

1.2 继承定义

1.2.1定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类
在这里插入图片描述
这个继承方式又该如何来理解呢?

1.2.2继承关系和访问限定符

继承方式和访问限定符都各有3个:
在这里插入图片描述
所以它们就会组成9种不同的情况:
在这里插入图片描述
那么我们该如何去理解这些呢?
1.基类的私有成员在派生类都是不可见。基类的其它成员在派生类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private
举个例子:
在这里插入图片描述
派生类继承下来的限制是这两种中小的那个。这里派生类中的_name和_age是protected。
2.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它
举个例子:
在这里插入图片描述
在这里插入图片描述
从上面两幅图中可以看出私有的和公有的是一样大的,所以私有的数据被继承下来了。
在这里插入图片描述
继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

3.基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
在这里插入图片描述
可以看出派生类中能访问,外面不能访问。以前我们写一个类时,private和protected,两者其实没有什么区别,现在在继承中就能体现它们的区别了。

4.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

5.在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强

2. 基类和派生类对象赋值转换

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。在这里插入图片描述
这里和不同类型的引用不一样,不同类型的引用会产生一个临时变量。但是这里不一样,它是语法天然支持的,没有类型转换。
子类赋值给父类的情况如下
在这里插入图片描述
代码演示:
在这里插入图片描述
从上图可以看出,已经赋值给父类了。这里的赋值是深拷贝,不是浅拷贝。

子类地址给父类指针的情况如下
在这里插入图片描述
是一个指针指向子类的一部分。
子类引用给父类的情况如下
在这里插入图片描述
它是子类一部分的别名。

基类对象不能赋值给派生类对象
在这里插入图片描述
强制类型转换也不行。

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run Time Type Information)的dynamic_cast 来进行识别后进行安全转换。(ps:这个我们后面再讲解,这里先了解一下)

3.继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域
我们知道:同一个域中不能有相同的名字,不同的域中就可以有相同的名字。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义
在这里插入图片描述
那么这里子类和父类都有_id,此时打印会打印谁的呢?
在这里插入图片描述
那么我们也可以在子类成员函数中,可以使用 基类::基类成员 显示访问
在这里插入图片描述
成员函数也是同样的道理,先调用子类自己的。
在这里插入图片描述
如果我们想调用父类的,指定作用域就行。
在这里插入图片描述
3.需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
在这里插入图片描述
这两个fun函数是重载还是隐藏关系呢?答案是:隐藏关系。原因是:函数重载要求在同一作用域,而这两个函数在不同的作用域里。
4.注意在实际中在继承体系里面最好不要定义同名的成员

4.派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?
在这里插入图片描述

4.1 构造

举个例子:
在这里插入图片描述
从这个例子中,我们可以看出什么呢?
子类构造函数原则:
a、调用父类构造函数初始化继承自父类成员

所以继承父类的成员_name还是用父类的构造函数来初始化。
在这里插入图片描述
b、自己再初始化自己的成员 – 规则参考普通类
析构、拷贝构造、复制重载也类似

子类的本身的成员还是去调用子类的来初始化。

那我们显示的写,又是什么情况呢
在这里插入图片描述
显示的去写,也没有办法去构造,只能显示的去传参数
在这里插入图片描述
这样写就像使用匿名对象方式。那么这样写,它是先调用的是子类的构造还是先调用父类构造呢
答案是:先调用父类的。原因是:初始化列表执行的顺序不是我们写的顺序,而是声明的顺序。在子类中,它是认为父类在前,子类在后

4.2 拷贝构造

拷贝构造也是相似的道理:
在这里插入图片描述
父类的成员变量拷贝时,会去调用父类的拷贝构造。子类的成员变量还是调用自己的拷贝构造。这里子类调用的是自己默认的拷贝构造。那么子类的拷贝构造我们该如何去显示写呢?
在这里插入图片描述
父类的显示定义是这样去写的。
在这里插入图片描述
因为拷贝构造是引用,所以我们可以把子类直接传给父类,这样就形成了切片:
在这里插入图片描述
所以它会自动的找子类里面父类成员来拷贝构造。那么什么时候去显示写的,就是当我们子类需要深拷贝的时候就要显示写了。

4.3 赋值

在这里插入图片描述
在这里插入图片描述
这里赋值也是相同的道理,父类的成员去调用父类的赋值函数。子类显示写如下:
在这里插入图片描述
这里就是显示的去调用父类的赋值函数。然后父类的赋值函数也是引用,可以把子类切片。但是这样写会出现栈溢出,原因就是这样写它调用的是子类自己的赋值函数,它把父类的隐藏了。我们要加上类域:
在这里插入图片描述
这里还有一个隐藏的切片,就是this。像s1=s3中,s3转到s会被切割,而this指针也会被切割(也就是s1也会切割)。

4.4 析构

我们先把子类的析构显示的来写:
在这里插入图片描述
我们发现这里显示调用发生了错误。原因是:父子类的析构函数构成隐藏关系,但可能有的同学就会问了,它们的名字不是不相同吗?原因是:多态的需要,析构函数名统一会被处理成destructor()
所以我们就这样写:
在这里插入图片描述
但这样还有一些情况:
在这里插入图片描述
我们只有s1和s2两个对象,子类调用了两次析构,但是父类调用了四次析构。这是为什么呢?
原因是:为了保证析构顺序:先子后父。子类析构函数完成后会自动调用父类析构函数,所以不需要我们显示调用
在这里插入图片描述
为什么一定是先子后父呢?因为父类先构造,然后子类再构造,析构时,子类先析构,父类再析构。然后我们如果要显示的来析构,可能就会导致父类先析构了。就违反了一些规则。

5. 如何设计一个不能被继承的类

我们可以把父类的构造函数设置成私有:
在这里插入图片描述
这样B就无法构造对象了。但此时A自己也不能被构造了啊,我们该怎么办呢?我们可以去写一个函数来构造:
在这里插入图片描述
但是调用类里面的函数必须要有对象,这就产生了死循环。我们可以这样:
在这里插入图片描述
在这里插入图片描述
这样父类自己还可以使用,子类如果继承父类,则不能构造使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学代码的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值