【C++进阶】C++继承概念详解

news2024/10/6 2:20:31

C++继承详解

  • 一,继承的概念和定义
    • 1.1 继承的概念
    • 1.2 继承的定义
    • 1.3 继承关系和访问限定符
  • 二,基类和派生类的对象赋值转移
  • 三,继承的作用域
  • 四,派生类的默认成员函数
  • 五,继承和友元&静态成员和继承
  • 六,菱形继承和菱形虚拟继承
    • 1. 菱形继承
    • 2. 菱形虚拟继承
  • 七,总结

一,继承的概念和定义

C++面向对象有三大特性,分别是封装,继承,多态。前面我们了解了封装,在这一部分我们来看一下继承。

1.1 继承的概念

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

看下面的代码:

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter"; // 姓名
	int _age = 18; // 年龄
};

class Student : public Person
{
protected:
	int _stuid; // 学号
};

class Teacher : public Person
{
protected:
	int _jobid; // 工号
};

int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

Student和Teacher这两个类继承了Person类,在Person类的基础上有增加了自己的属性,同时Person类中的成员变量和方法也都是两个子类的一部分,可以看到Student和Teacher类可以复用Person中的函数。

1.2 继承的定义

继承的格式:

派生类也叫子类,基类也叫父类
在这里插入图片描述


1.3 继承关系和访问限定符

继承方式和访问限定符有三种

在这里插入图片描述


实际上对基类成员的访问方式是由继承方式和成员变量共同决定的:

在这里插入图片描述

  1. 上面的表格我们会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),并且public > protected> private
  2. 基类private成员在派生类中无论以什么方式继承都是不可见的。也就是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
  3. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
  4. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

二,基类和派生类的对象赋值转移

基类和派生类的对象赋值转移也就是说派生类对象可以赋值给基类的对象指针/引用,形象地说是切片或切割,是将派生类中基类的部分切下赋给基类。

在这里插入图片描述


基类对象不能赋值给子类对象,但是基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。

三,继承的作用域

  1. 在继承体系中基类和派生类都有其自己的作用域
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 如果是成员函数的隐藏,只需要函数名相同就构成隐藏
class A
{
public:
	void fun()
	{
	cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		A::fun();//显式调用A中的fun
		cout << "func(int i)->" <<i<<endl;
	}
};
void Test()
{
	B b;
	b.fun(10);
};

这里可以看到A和B中都有fun函数,且同名,所以构成隐藏。但是注意,这两个fun可不是重载,因为不在同一个作用域

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

派生类也有默认成员函数,我们在前面讲过类的默认成员函数有六个
在这里插入图片描述

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序
  5. 派生类对象初始化先调用基类构造再调派生类构造

下面的代码可以看到上面所说的几个结论:

class Person
{
public :
	Person(const char* name = "peter")
		: _name(name )
	{
		cout<<"Person()" <<endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout<<"Person(const Person& p)" <<endl;
	}
	Person& operator=(const Person& p )
	{
		cout<<"Person operator=(const Person& p)"<< endl;
		if (this != &p)
			_name = p._name;
			return *this ;
	}
	~Person()
	{
		cout<<"~Person()" <<endl;
	}
protected :
	string _name ; // 姓名
};
class Student : public Person
{
public :
	Student(const char* name, int num)
		: Person(name )
		, _num(num )
	{
		cout<<"Student()" <<endl;
	}
	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout<<"Student(const Student& s)" <<endl ;
	}
	Student& operator = (const Student& s )
	{
		cout<<"Student& operator= (const Student& s)"<< endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this ;
	}
	~Student()
	{
		cout<<"~Student()" <<endl;
	}
protected :
	int _num ; //学号
};

五,继承和友元&静态成员和继承

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例

六,菱形继承和菱形虚拟继承

继承有单继承和多继承,单继承就是每个派生类只有一个直接基类,而多继承是指一个子类有两个或以上的直接父类的继承关系。而菱形继承是多继承的一种特殊情况。下面我们一起来探讨一下
在这里插入图片描述


在这里插入图片描述


1. 菱形继承

所谓菱形继承,就是如下图所示,Assistant继承了Student和Teacher,而继承的两个类又继承自Person.
在这里插入图片描述

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

从上面代码可以看出菱形继承会造成数据冗余和二义性的问题。在Assistant的对象中会有两份Person成员。

在这里插入图片描述

解决办法有两种,一种是显式地指定访问哪一个name,另一种是在上面的继承关系中,在Student和Teache的继承时采用虚拟继承

2. 菱形虚拟继承

下面是菱形虚拟继承的代码示例:

class A
{
public:
	int _a;
};

class B : virtual public A
{
public:
	int _b;
};

class C : virtual public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

虚拟继承会解决数据冗余和二义性

这是因为在菱形虚拟对象的模型中,A中的_a放到了D的最下面,同属于B,C。B,C又通过内部的指针(虚基表指针),指针指向的是一张表(虚基表),表里存放的是偏移量,根据偏移量就可以找到共有的_a.

在这里插入图片描述

七,总结

有了多继承也就有了菱形继承,菱形继承其实来说是个C++的缺陷,比较复杂。

除了继承之外,还有一种关系叫组合,继承实际上像一种is-a的关系,而组合更像是一种has-a的关系,

  1. 也就是说继承是每个派生类对象就是一个基类对象
  2. 而组合是说B组合了A,A包含在B中,每一个B对象都有一个A对象。

对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

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

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

相关文章

Ansys Lumerical | 激光雷达天线仿真

附件下载 联系工作人员获取附件 在本文中&#xff0c;我们将了解如何根据激光雷达应用需求设计和优化相控阵光栅天线。 概述 激光雷达&#xff08;LIDAR&#xff09;是“light detection and ranging”的简称&#xff0c;近年来由于在机器人、自动驾驶汽车、高精度测绘等领域…

【AcWing】蓝桥杯集训每日一题Day2|前缀和|562.壁画(C++)

562. 壁画 562. 壁画 - AcWing题库难度&#xff1a;中等时/空限制&#xff1a;1s / 64MB总通过数&#xff1a;4154总尝试数&#xff1a;10197来源&#xff1a;Google Kickstart2018 Round H Problem B算法标签 思维题枚举前缀和 题目内容 Thanh 想在一面被均分为 N 段的墙上画…

[java——基础] 双亲委派机制

目录 核心思想&#xff1a; 双亲委派机制的好处&#xff1a; 三种类加载器 解析源代码 双亲委派思想面试总结&#xff1a; 核心思想&#xff1a; 向上搜索&#xff0c;向下加载。 双亲委派机制的好处&#xff1a; 防止Java核心类被篡改&#xff0c;防止类的重复加载。 三…

哈希表|15.三数之和

力扣题目链接 int cmp(const void *a,const void *b) {return *(int*)a - *(int*)b;} int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {*returnSize 0;if(numsSize < 3)return NULL;qsort(nums, numsSize, sizeof(int),cmp);int **…

C++ Qt开发:QNetworkAccessManager网络接口组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QNetworkAccessManager组件实现Web网…

Java日志框架Log4j 2详解

目录 一、什么是日志&#xff1f; 二、日志的主要用途 三、常用日志框架 1、Apache Log4j 2、Commons Logging 3、SLF4J 4、Logback 5、JUL(Java Util Logging) 6、Log4j 2 四、log4j 2 的优点 五、Log4j 2下载和配置 1、访问Log4j – 下载 Apache Log4j™ 2官网&a…

RHEL9 DNF/YUM仓库管理软件包

DNF/YUM仓库管理软件包 一个基于RPM包的软件包管理器能够从指定的服务器自动下载RPM包并且安装&#xff0c;自动处理依赖性关系&#xff0c;并且一次性安装所有依赖的软件包C/S模式 Server服务端提供RPM软件包与数据库文件repodataClient客户端使用dnf仓库 常用组合 组合参…

你还可以通过“nrm”工具,来自由管理“npm”的镜像

你还可以通过“nrm”工具&#xff0c;来自由管理“npm”的镜像 nrm&#xff08;npm registry manager&#xff09;是npm的镜像管理工具&#xff0c;有时候国外的资源太慢&#xff0c;使用这个就可以快速地在npm源间切换。 1.安装nrm 在命令行执行命令&#xff0c;npm install…

Java 容器启动执行指定任务

1、实现CommandLineRunner接口 实现CommandLineRunner接口&#xff0c;注意做初始化任务的类需要放在扫描路径下&#xff0c;使用Component注入到spring容器中。 import com.zw.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; impo…

网络安全AI智能体公司「云起无垠」获数千万元天使+轮融资,致力于大模型与网络安全深度融合的技术研究

「云起无垠」致力于打造最懂安全的AI智能体&#xff0c;通过持续运营的工具、知识以及记忆引擎&#xff0c;不断提升智能体对用户安全场景的理解&#xff0c;以达到易于使用、自我学习、自主行动的特性&#xff0c;助力企业自动化执行各类安全任务&#xff0c;让软件更安全&…

在WSL2中安装多个Ubuntu教程

文章目录 前言一、前期准备1、WSL安装2、Docker安装 二、安装第二个Ubuntu系统1.切换为WSL22.获取Ubuntu16.04的tar文件从容器中导出tar 3. 将tar文件导入WSL4. 设置默认用户 总结 前言 适用于 Linux 的 Windows 子系统 (WSL) 是 Windows 的一项功能&#xff0c;可用于在 Wind…

H12-811_19

19.(多选题)如下图所示的网络&#xff0c;下列哪些命令可以使RouterA可以转发目的IP地址为10.0.3.3的效据包? A.ip route-static 10.0.3.3 255.255.255.255 10.0.12.2 B.ip route-static 10.0.2.2 255.255.255.255 10.0.12.2 ip route-static 10.0.3.3 255.255.255.255 10.0…

7、设计模式之桥接模式(Bridge)

一、什么是桥接模式 桥接模式是一种结构型设计模式。它将抽象部分和实现部分分离&#xff0c;使它们可以独立地变化。 二、角色组成 抽象部分&#xff08;Abstraction&#xff09;&#xff1a;定义了抽象部分的接口&#xff0c;并包含对实现部分的引用。 实现部分&#xff08;…

GPU技术文档汇总

GPU 进阶笔记&#xff08;二&#xff09;&#xff1a;华为昇腾 910B GPU 相关&#xff08;2023&#xff09;https://arthurchiao.art/blog/gpu-advanced-notes-2-zh/

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的铁轨缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发铁轨缺陷检测系统对于物流行业、制造业具有重要作用。本篇博客详细介绍了如何运用深度学习构建一个铁轨缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模…

做嵌入式编程,为什么用的是C语言而不是C++呢?

做嵌入式编程&#xff0c;为什么用的是C语言而不是C呢&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我…

【阿里云系列】-如何实现两个VPC网络资源互通

背景 由于实际项目预算有限&#xff0c;两套环境虽然分别属于不同的专有网络即不同的VPC&#xff0c;但是希望借助一台运维机器实现对两个环境的监控和日常的运维操作 网络架构 如下是需要实现的外网架构图&#xff0c;其中希望实现UAT环境的一台windows的堡垒机可以访问生产…

光学硬件——二向色片

二向色镜&#xff08;Dichroic Mirrors &#xff09;又称双色镜&#xff0c;常用于激光技术中。 产品介绍&#xff1a; 指45度入射或大角度入射时&#xff0c;把光源分离出特定的光谱改变部分光谱光路方向&#xff0c;常用于酶标仪器、荧光显微镜系统、投影光引擎系统、激光灯…

β2-肾上腺素能受体激动剂通过重建T细胞分化的稳态来纠正血小板减少-AbMole

免疫性血小板减少症&#xff08;ITP&#xff09;是一种自身免疫性出血性疾病&#xff0c;其特征是血小板的增加破坏和减少产生。ITP的发病机制尚未完全明了&#xff0c;但已知与自身抗体介导的血小板破坏和脾脏中Fc依赖性吞噬作用有关。 此外&#xff0c;T细胞免疫的异常在ITP的…

解决:黑马webpack视频中出现的问题总结

问题 1 ERROR in main Module not found: Error: Can‘t resolve ‘./src‘ 解决 Webpack 中 ERROR in main Module not found: Error: Can‘t resolve ‘./src‘ 问题 黑马AJAX-Node.js-Webpack教学视频&#xff08;BV1MN411y7pw 其中P98&#xff09;中webpack部分&#xff0c…