c++进阶之继承

一.继承的概念

在类的基础上进行扩展,扩展出方法(成员函数)和属性(成员变量), 进而形成新的类,被称为子类。(父类(子类公共的属性)单独封装一个类,让子类继承这个类,同时子类与子类间又做出细分(即每个子类里包含独属于它的成员变量或函数))。 这么干就实现了共有成员变量的复用,告别了冗余。

小试牛刀一下:

复制代码
#include<iostream>
using namespace std;
class Person
{//老师,学生两个类共有的成员变量以及成员函数,单独写成一个类,用于复用,告别冗余。
public:
	void identity()
	{
		cout << "void identity()" << _name << endl;
	}
protected:
	string _name = "张三";
	string _adderss;
	string _tel;
	int _year = 18;
};
class Student : public Person//复用
{
public:
	void study()
	{
		cout << "void study()" << endl;
	}
protected:
	int _stuid;
};
class Teacher : public Person
{
	void teach()
	{
		cout << "void teach()" << endl;
	}
protected:
	string _title;//职称
};
int main()
{
	Student s1;
	Teacher t1;
	s1.identity();
	t1.identity();
	return 0;
}

二.继承复用的格式

继承方式的细究

注:子类...成员指的是父类成员在子类中的访问方式。

继承方式,父类成员的限定符:public,protected,private

子类从父类继承来的成员,实际的访问方式并不由自己想要的继承方式(继承方式)单方面决定的,是由父类成员限定符和继承方式同时来决定:

父类其他成员(除private外)在子类访问方式:

public: 子类从父类中继承,子类里外都能对继承来的成员进行直接访问。

private,protected:能继承,子类里可直接访问,子类外无法直接访问。

父类private限定的成员在子类的访问方式:

能在子类里继承,但子类内外均无法直接访问。

但对于成员变量无法直接访问的情况,假如父类里某个成员函数在子类里或子类外能直接访问,则可以借助这个函数完成间接访问。

三.模板的继承

在实现栈时,可不用适配器那个模式,可以通过继承vector来复用相关成员。

注意:

要体现继承来的成员函数已实例化,需添加vevtor<T>。

四.父类和子类对象赋值兼容转换

注意:讨论下面内容的前提是继承方式为public!!!!!!!!

当子类实例化成对象时,能将子类对象(继承父类的相关成员)赋值给父类对象,就相当于一个切片:

代码实现:

当然,是不能将实例化父类对象赋值给子类对象的,因为父类缺乏子类拥有的成员,此时若赋值则无值可赋给子类对象:

但父类指针在一定程度上是允许赋值给子类指针的:

如上图,最终子类指针指向子类空间,在逻辑上也是合理的。因此会使用dynamic_cast(多态章会讲)来判断一下父类指针是指向子类还是父类的空间。若指向子类空间,会返回地址;若指向父类,则返回0,就无法赋值给子类指针。

五.继承的作用域

1.子类和父类各有各的作用域。

2.隐藏

此时若通过打印的方式来访问_num,究竟访问的是哪一个类里的呢?

答案是子类里的_num:

原因在于当子类和父类具有同名成员时,子类成员会对父类同名成员的直接访问做出屏蔽,这种情况叫隐藏。若要访问父类的成员,需使用父类::父类成员,指定作用域来访问。

访问父类同名成员:

父类和子类间成员函数满足隐藏的条件是函数名相同即可。

小试牛刀,but大坑:

一般看到函数名相同,参数类型不同,直接一个重载就上去了,但这是大错特错的。因为函数重载要求在同一作用域,但A类,B类在不同作用域,且B类继承了A类,而且两个成员函数的函数名又是相同的,那么就满足以上关于成员函数隐藏的定义,因此是隐藏关系。

注意:在继承关系的类之间,不到万不得已,不要定义同名成员,不然容易被隐藏坑到!!

六.子类默认成员函数

在看这部分前,需要有成员函数的积累,请看这篇文章第三部分

针对父类继承给子类的成员变量,系统就会去调用父类的默认成员函数

1.默认构造

父类不提供默认构造(无缺省值)的情况:

初始化列表显示调用:

2.拷贝构造

调用父类拷贝构造遇到的问题:

假如在子类拷贝构造函数的初始化列表里不写调用父类拷贝构造,且父类默认构造存在缺省值,那么就会自动去调用默认构造的缺省。

小结,关于构造函数和析构函数中子类&父类的执行顺序

构造函数:先调父类的构造函数,再调子类的。

析构函数:先析构子类再析构父类。

3.operator=

但上面的代码还是有问题的,会栈溢出。因为父类里跟子类一样有同名的operator=,与子类构成了隐藏,就会屏蔽父类的operator=,反复执行子类的该函数,造成了死循环,最终栈溢出。所以要调父类该函数,需要声明父类的类域:

那么这个问题是怎么发现的呢,就得提到一个功能------监视堆栈:

就积累到了经验,当出现栈溢出时,一般原因是递归深度过深:

可看到系统在不断调用子类的operator=,构成了一个死循环的递归,就栈溢出了

4.析构

wtf,父类析构调不动?

子类父类的析构函数名全是destructor(),既然构成隐藏就需要指定作用域,具体解释我们多态章再见吧。

但子类析构是不需要显示调用父类析构的:

七.实现不能被继承的类

八.友元继承

测试友元函数能否被子类继承:

即Display是Person这个父类的友元函数,但不是Student这个子类的友元函数。

想要父类友元函数访问的到子类的成员,只需在子类里也做该函数的友元声明即可:

插曲:

在测试友元是否能被继承时,会出现这样一个报错:

明明友元函数声明的形参处是不缺乏逗号的,那为什么还会这样报错?

编译器遇到函数,变量,类型的时候,为了增加效率,只会向上去寻找它们定义的代码。这时由于子类Student是定义在友元声明的下面的,那么向上去找固然找不到子类,就报错了。

此时需要在友元声明前再加一个子类的前置声明。

九.static成员的继承

十.多继承

1.三种继承方式

单继承:一个子类只有一个直接父类的继承关系。

多继承:一个子类有两个及其以上的直接父类的继承关系

在子类名称后写继承方式时,用多个逗号连接不同继承方式的父类 就行了

菱形继承:一个子类有两个直接父类,同时两个直接父类又同时有一个相同的直接父类。

数据冗余与二义性

由于Student类与Teacher类继承了同一父类Person,那么当Assistant类的对象访问Person类的成员时,究竟是通过Student类,还是Teacher类,这说不清楚,就产生了二义。同时两个类分别继承同一父类还造成了空间的浪费。

解决二义性的方式
1.访问时包含类域:

菱形继承实用的地方:io库

为了使iostream同时继承istream和ostream的成员,只能这么干。

为了同时解决二义性和冗余的问题,我们引入虚继承。

2.虚继承

在拥有冗余父类的两个子类前加virtual ,就能使它俩共用同一个空间的父类,使得重复的两份合二为一

插曲

下面这个图,算是菱形继承吗?

答案是当然算,因为类E访问类A的时候,依旧存在着A类的成员,由哪个子类继承的二义性 。还有B,C在不同空间同时继承A的冗余性

那么用虚继承解决这两个问题时,virtual加在哪呢?

答案是B和C

原因在于,哪个类产生了数据二义性和冗余,就该在继承它的两个子类处添加virtual。

尽量不要去设计菱形继承,底层和逻辑上水太深,把握不住的!!!!!!!!

十一.继承和组合

如果父类子类间是继承关系,父类会将所有public或protected的成员开放给子类,那么它俩耦合度过高,父类稍微改动,就会对子类影响很大。

而组合关系里,父类仅开放部分接口给子类,那么它俩耦合度就很低。父类改动,子类影响就很小。

相关推荐
yu85939582 小时前
C++ 虚拟磁盘与虚拟光驱实现
开发语言·c++
Tingjct2 小时前
C++ 多态
java·开发语言·c++
lclin_20202 小时前
大恒Galaxy SDK MFC开发(1):枚举、打开、开始_停止采集基础流程(无画面版)
c++·机器视觉·工业相机·大恒相机·galaxysdk
初圣魔门首席弟子3 小时前
bug20260415
c++·bug
m0_716765233 小时前
数据结构--循环链表、双向链表的插入、删除、查找详解
开发语言·数据结构·c++·学习·链表·青少年编程·visual studio
不想写代码的星星4 小时前
类型萃取:重生之我在幼儿园修炼类型学
开发语言·c++
郝学胜-神的一滴4 小时前
中级OpenGL教程 001:从Main函数到相机操控的完整实现
c++·程序人生·unity·图形渲染·unreal engine·opengl
charlie1145141914 小时前
嵌入式现代C++教程实战篇第12篇:C宏时代的LED驱动 —— 能跑但不优雅
c语言·c++·stm32·单片机·嵌入式硬件·c
wunaiqiezixin4 小时前
链表多项式大整数-BigInt
数据结构·c++·链表