C++:继承:面向对象编程的重要特性

在这里插入图片描述
(❁´◡❁)(●'◡'●)╰(*°▽°*)╯(*/ω\*)(^///^)(❁´◡❁)(❁´◡❁)(●'◡'●)╰(*°▽°*)╯(*/ω\*)(❁´◡❁)(●’◡’●)╰(°▽°)╯(/ω\)(///)

C++:继承:面向对象编程的重要特性

    • 前言
    • **继承**
      • 1.继承的概念及定义
        • 1.1继承的概念
        • 1.2继承的定义
          • 1.2.1定义的格式
          • 1.2.2继承关系和访问限定符
          • 1.2.3继承后基类(父类)成员访问方式的变化
      • 2.基类和派生类对象赋值转换
      • 3.继承中的作用域
      • 4.派生类的默认成员函数
      • 5.继承与友元
      • 6. 继承与静态成员
      • 7.多继承与菱形继承和菱形虚拟继承

🎉文章简介:

🎉本篇文章将 C++继承,全特化,偏特化,非类型模板参数,模板的分离编译 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦!!!

前言

在这篇文章中,你可以学习到继承作为面向对象编程的重要特性,它允许一个类(子类)继承另一个类(父类)的属性和方法。
在文章中涵盖的主要内容:
继承的概念,继承的语法,继承的作用,单继承,多继承,菱形继承,菱形虚拟继承,菱形虚拟继承与菱形继承在内存中的差异等。

继承

1.继承的概念及定义

1.1继承的概念

继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。简单地说就是类的复用;

1.2继承的定义
1.2.1定义的格式

举个栗子(下附代码):
在这里插入图片描述
代码段:

class Person
{
protected:  
	string _name;
	int _age;
	string _sex;
	int _tel;
};
class student:public Person     //student继承了Person这个类
{
protected:
	int _id;
};
1.2.2继承关系和访问限定符

在这里插入图片描述

1.2.3继承后基类(父类)成员访问方式的变化
类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

总结:

1.基类的private成员在派生类中无论以什么方式继承都是不可见的,这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它;

2.基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的;

3.实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式)public > protected > private;

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

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

举个栗子:
在这里插入图片描述
从上面例子可以看出,private继承后,public,protected成员在派生类可以访问,private成员不可访问;

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

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割
基类对象不能赋值给派生类对象。
基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。注意:但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,会有特殊处理(后面的文章会提到);

在这里插入图片描述

class Person
{
protected:  
	string _name;
	int _age;
	string _sex;
	int _tel;
};
class student:public Person
{
protected:
	int _id;
};

int main()
{
	student s1;
	Person p1;

	p1 = s1;
	//s1 = p1;   //这里不能基类给派生类

	Person& pp1 = s1;     //是对截断后的内容引用
	Person* pp2 = &s1;     //指向的是截断后的内容

	student* ss;
	ss = (student*)&p1;         //基类的指针可以通过强制类型转换赋值给派生类的指针
	return 0;
}

在这里插入图片描述

3.继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域
(就凭这一点,因此两个域中的函数不能构成函数重载
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
4. 注意在实际中在继承体系里面最好不要定义同名的成员。

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

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。(先父后子)
派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
派生类的operator=必须要调用基类的operator=完成基类的复制。
派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。(先子后父)
编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
如果要显示调用,需要指定作用域;

5.继承与友元

友元关系不能被继承,也就是说基类友元不能访问子类私有和保护成员

举个例子:
在这里插入图片描述

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。因为静态成员函数不是放在类里面的,而是放在静态区的,是这个类的对象所公有的,无论派生出多少个子类,都只有一个static成员实例 ;
利用这个特性,可以统计这个类被创建了多少个对象;
如果被继承了,无论派生类实例化出对象了还是基=基类实例化出对象都会调用基类的构造函数,就可以统计出个数;

例子:


class Person
{
public:
	Person()
	{
		_count++;
	}
protected:  
	int _age;
public:
	static int _count;
};
int Person::_count = 0;

class student:public Person
{
public:
	student()
	{}
protected:
	int _id;
};

int main()
{
	student S1;
	student S2;
	Person P1;
	Person P2;

	cout << Person::_count << endl;    //打印结果为4
	return 0;
}

7.多继承与菱形继承和菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类只有多个直接父类时称这个继承关系为多继承

在这里插入图片描述

菱形继承:
在这里插入图片描述

菱形继承的问题:菱形继承有数据冗余二义性的问题。在D的对象中A成员会有两份。因为B,C中都继承了A,里面各有一份A,然后D继承了B和C,所以会有两份A;

因为有两份,所以数据冗余了,又因为D里面有两份A的成员,如果想通过D实例化的对象去访问B中继承的A的成员时,不知道是去访问B的还是C的;结合下面例子看看:

结合下面代码:
在这里插入图片描述

class A
{
public:
	int _a;
};
class B:public A
{
protected:
	int _b;
};
class C:public A
{
protected:
	int _c;
};
class D:public B, public C
{
protected:
	int _d;
};
int main()
{
	D dd;
	//dd._a = 10;      //会报错(访问不明确)
	dd.B::_a = 10;
	dd.C::_a = 100;     //这样指定类域访问也可以,但是代码稍许冗余
	return 0;
}

解决方法:

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在B和C的继承A时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。

解决后的代码:

class A
{
public:
	int _a;
};
class B:virtual public A    /这里+
{
protected:
	int _b;
};
class C:virtual public A   //这里+
{
protected:
	int _c;
};
class D:public B, public C
{
protected:
	int _d;
};

在哪里+virtual的问题

在造成数据冗余的那两个类的继承方式前面加virtual

比如:
在这里插入图片描述

菱形继承虚拟菱形继承在内存上的差异
我们讨论的是VS上

D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
在这里插入图片描述
(❁´◡❁)(●'◡'●)╰(*°▽°*)╯(*/ω\*)(^///^)(❁´◡❁)(●’◡’●)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学完Python的7大就业方向,哪个赚钱最多?

“ 我想学Python&#xff0c;但是学完Python后都能干啥 &#xff1f;” “ 现在学Python&#xff0c;哪个方向最简单&#xff1f;哪个方向最吃香 &#xff1f;” “ …… ” 相信不少Python的初学者&#xff0c;都会遇到上面的这些问题。大家都知道Python很吃香&#xff0c;薪资…

英伟达 V100、A100/800、H100/800 GPU 对比

近期&#xff0c;不论是国外的 ChatGPT&#xff0c;还是国内诸多的大模型&#xff0c;让 AIGC 的市场一片爆火。而在 AIGC 的种种智能表现背后&#xff0c;均来自于堪称天文数字的算力支持。以 ChatGPT 为例&#xff0c;据微软高管透露&#xff0c;为 ChatGPT 提供算力支持的 A…

slab分配器

什么是slab分配器&#xff1f; 用户态程序可以使用malloc及其在C标准库中的相关函数申请内存&#xff1b;内核也需要经常分配内存&#xff0c;但无法使用标准库函数&#xff1b;linux内核中&#xff0c;伙伴分配器是一种页分配器&#xff0c;是以页为单位的&#xff0c;但这个…

【考研数学】零基础全年可实操规划+网课资料分析

我理解的零基础就是一点基础也没有&#xff0c;甚至来呢最基础的求极限都不会 其实大家可以参照我的经验&#xff0c;零基础数学二&#xff0c;经过一年的学习学习&#xff0c;最后考了122分 首先大家要有自信&#xff0c;我都行你们肯定也可以&#xff01;其实考研数学真的没…

XCode升级错误:Command CompileC failed with a nonzero exit code 解决办法

升级完XCode之后&#xff0c;bulid失败&#xff0c;出现如下错误&#xff1a; 问题1&#xff1a; xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrunCommand Compi…

【性能测试】JMeter:集合点,同步定时器的应用实例!

一、集合点的定义 在性能测试过程中&#xff0c;为了真实模拟多个用户同时进行操作以度量服务器的处理能力&#xff0c;可以考虑同步虚拟用户以便恰好在同一时刻执行操作或发送请求。 通过插入集合点可以较真实模拟多个用户并发操作。 (注意&#xff1a;虽然通过加入集合点可…

2024藏在身边的冷门暴利行业,普通人掌握这个方式也能轻松加入赚取100万!适合普通人轻资产创业项目!2024新的创业机会

如果说2024创业圈流传最多的话是什么&#xff0c;那一定是&#xff1a;“阶级固化了&#xff0c;翻身太难”、“2024机会太少了&#xff0c;赚不到钱”、“经济大环境不好&#xff0c;创业寒冬”云云。 其实这些抱怨都是毫无道理的&#xff0c;无论是国内还是国外&#xff0c;对…

突破图神经网络技术瓶颈!新阶段3大创新方向大幅提高模型性能

针对传统的图神经网络在处理非结构化数据、捕捉高阶关系等方面的局限性&#xff0c;研究者们提出了众多优化方案。 这其中&#xff0c;超图神经网络、几何图神经网络、动态图神经网络作为GNN发展的前沿方向&#xff0c;不仅提供了更加丰富和灵活的方法来处理各种复杂的图数据&…

基于ssm的航空售票系统

基于SSM的航空订票系统根据功能设计划分为管理员用户和注册用户&#xff0c;从这两种用户的功能所需展开设计&#xff0c;管理员对注册用户管理、航班管理、航班时刻管理、通知公告管理、订票管理、退票管理等&#xff1b;注册用户主要是注册成功后登录&#xff0c;机票查询&am…

基于 Vue3打造前台+中台通用提效解决方案(中)

33、实现全屏展示功能 我们知道在原生dom上,提供了一些方法来供我们开启或关闭全屏: Element.requestFullscreen()Document.exitFullscreen()Document.fullscreenDocument.fullscreenElement一般浏览器 使用requestFullscreen()和exitFullscreen()来实现 早期版本Chrome浏…

异常:程序出现的问题

目的&#xff1a;为了以后发现异常后怎么去处理 异常的作用

学点儿Java_Day7_在实体类当中IDEA无法进行单元测试(@Test没有启动按钮)

在敲代码体会继承和访问修饰符的时候忽然遇到了单元测试不管用的情况&#xff0c;表现为没有启动按钮   经过一番折腾&#xff0c;发现我的测试是在具有构造函数的实体类Person当中进行的&#xff0c;当我把所有的构造函数删除后&#xff0c;启动按钮又出来了&#xff0c;加…

鸿蒙开发实战:【文件管理】

介绍 本示例主要展示了文件管理相关的功能&#xff0c;使用[ohos.multimedia.medialibrary]、[ohos.filemanagement.userFileManager] 、[ohos.fileio] 、[ohos.file.fs]、[ohos.app.ability.contextConstant] 等接口&#xff0c;实现了增添文件、删除文件、查找指定类型文件…

Java基础夯实——八股文【2024面试题案例代码】

1、Java当中的基本数据类型 Java中常见的数据类型及其对应的字节长度和取值范围如下&#xff1a; byte&#xff1a;1字节&#xff0c;取值范围为-128到127。short&#xff1a;2字节&#xff0c;取值范围为-32,768到32,767。int&#xff1a;4字节&#xff0c;取值范围为-2,147…

【Linux】初识进程

目录 操作系统是什么 设计操作系统的目的 操作系统的定位 如何理解管理 管理的本质 管理的例子 计算机的管理概念图 操作系统管理逻辑的六字真言 系统调用和库函数的概念 进程 进程的概念 什么是PCB&#xff1f; PCB的主要内容 如何查看进程&#xff1f; 通过系统…

数据结构与算法----复习Part 17 (图(Graph)初步)

本系列是算法通关手册LeeCode的学习笔记 算法通关手册&#xff08;LeetCode&#xff09; | 算法通关手册&#xff08;LeetCode&#xff09; (itcharge.cn) 目录 一&#xff0c;图&#xff08;Graph&#xff09; 图的分类 顶点的度 环形图和无环图 连通图和非连通图 强连…

远程服务器安装portainer —— docker的可视化工具

可视化工具&#xff08;了解即可&#xff09; 最常用的工具是 portainer portainer 是一个开源的容器管理平台&#xff0c;它提供了一个简单易用的用户界面&#xff0c;用于管理和监控 docker 容器集群。通过 portainer&#xff0c;用户可以轻松地进行容器的部署、启动、停止…

C++_回文串

目录 回文子串 最长回文子串 分割回文串 IV 分割回文串 II 最长回文子序列 让字符串成为回文串的最少插入次数 回文子串 647. 回文子串 思路&#xff0c;i j表示改范围内是否为回文串&#xff0c; ②倒着遍历是为了取出dp[i 1][j - 1] ③i j 只有一对&#xff0c;不会重复…

算法沉淀——贪心算法五(leetcode真题剖析)

算法沉淀——贪心算法五 01.跳跃游戏 II02.跳跃游戏03.加油站04.单调递增的数字 01.跳跃游戏 II 题目链接&#xff1a;https://leetcode.cn/problems/jump-game-ii/ 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转…

一共有哪 3 类线程安全问题

一共有哪 3 类线程安全问题 运行结果错误发布和初始化导致线程安全问题活跃性问题死锁活锁饥饿 要想弄清楚有哪 3 类线程安全问题&#xff0c;首先需要了解什么是线程安全&#xff0c;线程安全经常在工作中被提到&#xff0c;比如&#xff1a;你的对象不是线程安全的&#xff0…