C++之继承(通俗易懂版)

news2024/9/21 4:36:22

前言:我们都知道C++是一门支持过程化编程,面向对象的高级语言,既然是面向对象的语言,那么对于对象而言,对象会有很多中相同的属性,举个例子:你和你老师,你们都有着共同的属性和身份,例如学生和老师都有性别,年龄这个成员变量,但你们又有着差异,你们可能学生有学号,老师有工号,老师可能还有职称,所以我们可以想象一下如何我们定义一个关于学生和老师的类,对于老师和学生是不是都有着共同点(都有年龄和性别),也有着不同点,所以如果我们定义老师类之后,又定义一个学生类,学生类是不是和老师类有一些成员变量重复了,可能有老铁认为这几个成员相同没啥太大的影响,但你想想一个工程项目得有多少个文件和多少个类,每一个类都有几个重复的成员变量和成员函数,是不是会导致代码运行效率大大降低了,也会占用更多的内存。为了解决这个问题,C++提出了继承的概念

继承:

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

简单的类的继承

语法格式:class 子类名 : 继承方式(一般是公有继承) 父类名
//基类(父类)
class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter"; // 姓名
	int _age = 18; // 年龄
};
//派生类(子类)
//class 子类名 : 继承方式(一般是公有继承) 父类名
class Student : public Person//子类对父类的继承
{
protected:
	int _stuid; // 学号
};
//派生类(子类)
//class 子类名 : 继承方式(一般是公有继承) 父类名
class Teacher : public Person//子类对父类的继承
{
protected:
	int _jobid; // 工号
};
int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

在这里插入图片描述
调试看看派生类是否含有基类的成员变量
在这里插入图片描述

类的继承方式:

(1)private(基类的成员是private):如果基类成员(成员函数和成员变量)都是私有继承,那么派生类虽然继承基类,但基类成员在派生类中和在派生类外都是不可见。
在这里插入图片描述
(2)protected(基类的成员是protected):派生类继承基类,虽然在派生类外不能访问基类成员,但在派生类中可以访问基类成员
在这里插入图片描述
(3)public(基类的成员是public):派生类继承基类,既可以在派生类中访问基类成员,也可以在派生类外访问基类成员。

1.通过以上的对于三种访问限定符的分析,我们知道访问范围:public>protected>private
2.派生类使用class关键字时默认是private继承,struct关键字默认是public继承。(但是建议还是写出继承方式比较好),在实际中还是推荐使用public继承方式,其他俩种继承方式实用性不强。

基类和派生类对象赋值的转换

(1)派生类可以赋值给基类对象/基类指针/基类引用(形象的叫法是切割:把派生类中和父类相似的一部分切割出来赋值给基类)
在这里插入图片描述

(2)基类不能赋值给派生类
在这里插入图片描述

继承的作用域

基类和派生类都是独立的类,都有着自己独立的作用域,但是如果基类和派生类有着同名的成员,那么会出现什么情况呢?


//基类(父类)
class Person
{
protected:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter"; // 姓名
	int _age = 18; // 年龄
};
class Student : public Person//子类对父类的继承
{
protected:
	int _age = 17; // 年龄
};

int main()
{
	Student s;
	return 0;

}

你们看看这个代码能不能运行
在这里插入图片描述
运行没有任何问题,但是这个代码却非常容易混淆
Student的_age和Person的_age构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆

我们再看看这段代码

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		//这里是想调用基类的成员函数fun()
	    fun();
		cout << "func(int i)->" << i << endl;
	}
};
void Test()
{
    B b;
    b.fun(10);
};

int main()
{
	Test();
}

在这里插入图片描述
编译器却是调用派生类的fun函数,这是为什么呢?有的老铁可能会认为这两个函数构成重载了,但请老铁们注意,这两个函数并不是在同一个作用域,所以它们并不构成重载,它们函数名相同,是构成隐藏了。

隐藏(重定义):子类和父类有同名成员时,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		//这里是想调用基类的成员函数fun()
	    A::fun();
		cout << "func(int i)->" << i << endl;
	}
};
void Test()
{
    B b;
    b.fun(10);
};

int main()
{
	Test();
}

在这里插入图片描述
所以在继承体系中我们尽量不要定义同名的成员。

派生类的默认成员函数:

无论是何种成员函数,派生类都会调用基类的默认成员函数去把从基类继承的成员进行相对应得操作,例如:派生类的构造函数会自动去调用基类的构造函数,从而完成对派生类中从基类继承下来的成员的构造,如果基类没有默认构造函数,则必须在派生类构造函数的初始化列表阶段显示调用基类的自定义的构造函数。
在这里插入图片描述
对于析构函数,会先调用派生类的析构函数,再调用基类的析构函数,因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

派生类对象初始化先调用基类构造再调派生类构造

继承与友元:

基类中如果有友元函数,派生类继承基类却不能继承基类的友元函数,也就是说基类友元不能访问子类的私有和保护成员。

class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}
void main()
{
	Person p;
	Student s;
	Display(p, s);
}

在这里插入图片描述

继承与静态成员:

如果基类中有静态成员,则整个体系中唯一只有这个静态成员,无论派生出多少个子类,都只有这一个static成员实例。

复杂的菱形继承及菱形虚拟继承

单继承:一个派生类只有一个直接基类
多继承:一个派生类有两个或两个以上的直接基类

菱形继承(多继承的特殊情况):
在这里插入图片描述
二义性:

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";
}

int main()
{
	Test();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

解决二义性

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a.Person::_name = "peter";
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

int main()
{
	Test();
	return 0;
}
虚拟继承可以解决菱形继承的数据冗余和数据二义性
class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A//在继承方式前加上virtual关键字就构成虚拟继承
{
public:
	int _b;
};
// class C : public A
class C : virtual public A//在继承方式前加上virtual关键字就构成虚拟继承
{
public:
	int _c;
};
class D : public B, public C//在继承方式前加上virtual关键字就构成虚拟继承
{
public:
	int _d;
};

虚拟继承的原理(以上面代码为例子):
虚拟继承通过把D对象中的A放到最下面,然后B和C中的成员变量各自放好,在B和C中不只有成员变量,还有各存放一个指针,各指向一张表,用于寻找公共的A,这两个指针叫虚机表指针,这两个表叫虚机表,虚机表中存放着A和B,C的相对的偏移量,B和C都可以通过各自的偏移量去寻找到公共的A
在这里插入图片描述

继承与组合

在工程项目都是提倡高内聚,低耦合,那什么是高内聚,什么又是低耦合呢?举个例子,一个部门一般是都是有很多个团队,每个团队实现项目中的一部分功能,我们希望每个团队实现的功能能够相对独立性比较强,便于我们维护,这个就是低耦合,高内聚是多个团队充分发挥合作,把项目完整和高效的实现出来!

继承:是低内聚,高耦合,允许你根据基类的实现来定义派生类的实现,这种生成派生类的复用被称为白箱复用,基类的内部细节是对子类开放的,所以继承一定程度上破环了基类的封装,基类的改变,对派生类有

组合:是高内聚,低耦合,组合是黑箱复用,这种复用是对象内部细节不可见的。

Is-a 关系:也称为继承,如果一个类是另一个类的一种类型,那么就存在 is-a 关系。
例如:如果你有一个 Vehicle 类和一个 Car 类,而 Car 是 Vehicle 的一种,那么 Car 就可以继承自 Vehicle。这意味着 Car 类拥有 Vehicle 类的所有属性和方法,并且还可以添加自己的特定属性和方法。

Has-a 关系:也称为聚合或组合。当一个类包含另一个类的对象作为其成员时,就是 has-a 关系。
例如,假设你有一个 Car 类和一个 Engine 类。每个 Car 都有一个 Engine,所以你可以将 Engine 对象作为一个成员变量放在 Car 类中。
这种关系不是通过继承来表达的,而是通过在一个类中声明另一个类类型的实例变量来实现。

// Car和BMW Car和Benz构成is-a的关系
class Car {
protected:
	string _colour = "白色"; // 颜色
	string _num = "陕ABIT00"; // 车牌号
};
class BMW : public Car {
public:
	void Drive() { cout << "好开-操控" << endl; }
};
class Benz : public Car {
public:
	void Drive() { cout << "好坐-舒适" << endl; }
};
// Tire和Car构成has-a的关系
class Tire {
protected:
	string _brand = "Michelin"; // 品牌
	size_t _size = 17; // 尺寸
};
class Car {
protected:
	string _colour = "白色"; // 颜色
	string _num = "陕ABIT00"; // 车牌号
	Tire _t; // 轮胎
};

总结:这篇文章我们分享了继承的概念,继承的方式,继承的赋值,继承的作用域,派生类的默认成员函数,继承与友元函数和静态成员,还有复杂的菱形继承和菱形继承的解决方式虚拟继承,最后是继承和组合的对比。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2144883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Longman Dictionary of Contemporary English (朗文当代高级英语辞典)

Longman Dictionary of Contemporary English {朗文当代高级英语辞典} 1. Longman Dictionary of Contemporary English1.1. school References 1. Longman Dictionary of Contemporary English https://www.ldoceonline.com/ 1.1. school https://www.ldoceonline.com/dicti…

C++: 高效使用智能指针的8个建议

前言&#xff1a;智能指针是C11的新特性&#xff0c;它基于RAII实现&#xff0c;可以自动管理内存资源&#xff0c;避免内存泄漏的发生&#xff0c;但是智能指针也并不是万能的&#xff0c;如果不正确使用智能指针&#xff0c;也会导致内存泄漏的发生&#xff0c;因此&#xff…

嵌入式人工智能项目及人工智能应用项目——大合集列表查阅

本文的项目合集列表可能更新不及时&#xff08;会及时更新&#xff09;&#xff0c;可查阅实时更新的链接如下。 嵌入式人工智能及人工智能应用项目合集实时更新链接如下&#xff1a; 阿齐嵌入式人工智能及人工智能应用项目合集 (kdocs.cn)https://www.kdocs.cn/l/cc97tuieys4…

MacOS安装MAT教程

MAT下载地址MAT下载地址MAT下载地址MAT下载地址 如果不知道你的芯片类型, 可以执行如下命令 uname -m

Fiddler抓包工具实战

文章目录 &#x1f7e2; Fiddler入门到精通&#x1f449;主要功能&#x1f449;使用场景 &#x1f7e2; 一、Fiddler抓包和F12抓包对比&#x1f7e2; 二、Fiddler的核心功能&#x1f7e2; 三、Fiddler的工作原理&#x1f7e2; 四、Fiddler功能配置使用&#x1f449;规则设置&am…

力扣 11题 盛最多水的容器

前言&#xff1a;这道题其实挺有意思的&#xff0c;前段时间在刷视频的时候看都了很多博主都在讲解这道题&#xff0c;最近在写一些算法的东西&#xff0c;我就顺势刷到了这道题。在这里写一写我自己的对这道题的理解。 题目详见&#xff1a;https://leetcode.cn/problems/con…

【DAY20240918】03教你轻松配置 Git 远程仓库并高效推送代码!

文章目录 前言 git diff一、远程仓库&#xff1f;1、在 Gitee 上新建仓库&#xff1a;2、Git 全局设置&#xff1a;3、添加远程仓库&#xff1a;4、推送本地代码到远程仓库&#xff1a;5、输入用户名和密码&#xff1a;6、后续推送&#xff1a; 二、全情回顾三、参考 前言 git …

谷歌浏览器扩展程序怎么提升CSS开发效率

在现代Web开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;是不可或缺的一部分&#xff0c;它负责网页的视觉呈现和布局设计。为了提高CSS开发的效率&#xff0c;谷歌浏览器提供了许多实用的扩展程序。本文将介绍几个关键的扩展程序&#xff0c;并探讨如何利用它们来优…

网络安全学习(五)Burpsuite实战

bp功能确实强大&#xff0c;记录一个bp手机验证码的实例。 当然&#xff0c;首先要打开bp&#xff0c;设置好浏览器的代理。 浏览器访问实例网址www.xxx.com&#xff08;隐藏真实网址&#xff09;。 真实网址有个注册功能&#xff0c;需要手机验证码。 好的&#xff0c;我们…

深入探索Android开发之Java核心技术学习大全

Android作为全球最流行的移动操作系统之一&#xff0c;其开发技能的需求日益增长。本文将为您介绍一套专为Android开发者设计的Java核心技术学习资料&#xff0c;包括详细的学习大纲、PDF文档、源代码以及配套视频教程&#xff0c;帮助您从Java基础到高级特性&#xff0c;再到A…

『功能项目』回调函数处理死亡【54】

我们打开上一篇53伤害数字UI显示的项目&#xff0c; 本章要做的事情是使用回调函数处理怪物Boss01死亡后增加主角经验值的功能&#xff0c;以及生成一个七秒的升级特效 首先增加一个技能特效重命名为PlayerUpGradeEffect 修改脚本&#xff1a;BossCtrl.cs 修改脚本&#xff1a…

SpringBoot2:web开发常用功能实现及原理解析-@ControllerAdvice实现全局异常统一处理

文章目录 前言1、工程包结构2、POM依赖3、Java代码 前言 本篇主要针对前后端分离的项目&#xff0c;做的一个统一响应包装、统一异常捕获处理。 在Spring里&#xff0c;我们可以使用ControllerAdvice来声明一些关于controller的全局性的东西&#xff0c;其用法主要有以下三点…

PostgreSQL技术内幕11:PostgreSQL事务原理解析-MVCC

文章目录 0.简介1.MVCC介绍2.MVCC常见的实现方式3.PG的MVCC实现3.1 可见性判断3.2 提交/取消 0.简介 本文主要介绍在事务模块中MVCC(多版本并发控制&#xff09;常见的实现方式&#xff0c;优缺点以及PG事务模块中MVCC&#xff08;多版本并发控制&#xff09;的实现。 1.MVCC…

1×1卷积核【super star 卷积核】

一、11卷积的作用 我们先来给出11卷积的一般作用&#xff0c;如下所示&#xff1a; • 跨通道的特征整合 • 特征通道的升维与降维 • 减少权重参数&#xff08;卷积核参数&#xff09; 【 简化模型 】 1.1 特征通道的升维与降维/跨通道的特征整合/简化模型 输入数据&…

【Transformer深入学习】之一:Sinusoidal位置编码的精妙

看苏神的文章提到&#xff1a;Transformer原论文使用Sinusoidal位置编码&#xff0c;作为位置编码的一个显式解&#xff0c;Google 在原论文中对它的描述寥寥无几&#xff0c;只是简单提及了它可以表达相对位置信息&#xff0c;并未提及这个编码的合理性。 看了几篇文章&#x…

JAVA零基础入门——面向对象

1.继承 1.1 继承概念 继承的概念&#xff1a;继承就是子类继承父类的特征和行为&#xff0c;使得子类对象&#xff08;实例&#xff09;具有父类的实例域和方法&#xff0c;或子类从父类继承方法&#xff0c;使得子类具有父类相同的行为。我们将"继承概念"分为两类:…

WINDOWS AGENTARENA:EVALUATING MULTI-MODAL OS AGENTS AT SCALE论文学习

文章开头说现有的agent都是局限于特定领域&#xff08;什么网络问答啊&#xff0c;仅限文字啊&#xff0c;仅限于某一个app啊&#xff09;这样的&#xff0c;本文的工作主打一个贴近用户使用场景&#xff0c;用户用什么软件&#xff0c;看什么网页&#xff0c;本文的模型就用什…

Sapiens——人类视觉大模型的基础

引言 大规模预训练以及随后针对特定任务的语言建模微调取得了显著成功&#xff0c;已将这种方法确立为标准做法。同样&#xff0c; 计算机视觉方法正逐步采用大规模数据进行预训练。LAION5B、Instagram-3.5B、JFT-300M、LVD142M、Visual Genome 和 YFCC100M 等大型数据集的出现…

Python 实现Excel XLS和XLSX格式相互转换

在日常工作中&#xff0c;我们经常需要处理和转换不同格式的Excel文件&#xff0c;以适应不同的需求和软件兼容性。Excel文件的两种常见格式是XLS&#xff08;Excel 97-2003&#xff09;和XLSX&#xff08;Excel 2007及以上版本&#xff09;。本文将详细介绍如何使用Python在XL…

【SQLite数据库常规使用命令】

之前在做围绕数据库相关的一些小工具的时候&#xff0c;想找一款数据库作为小工具的资料库。需求是&#xff1a;不用复杂的安装&#xff0c;支持简单SQL&#xff0c;空间占用小&#xff0c;操作简单等等。 结合着之前接触到的一些研发同事做的产品的使用经验&#xff0c;我想到…