C++继承(图文非常详细)

继承的概念

1.什么是继承

1.简单定义

我们来看一下下面这串代码注意其中的两个类father 和 son

using namespace std;
#include<iostream>
class father
{
public:void definity(){cout << "father" << endl;}
protected:int tall = 180;int age = 20;
};
class son : public father
{
public:void definityson(){cout << "son" << endl;cout << tall << endl;}
protected:int tall1 = 200;int age1 = 25;
};
int main()
{son SN;SN.definity();SN.definityson();}

 我们可以发现son类定义的SN可以调用father的函数definity()

 

如上图所示,这便是继承的格式。

 class 类名: 继承方式  继承的类名

 通过这样继承的方式我们可以想想一下,就是将father里面的内容全部让son可见,如果你觉得不好理解,你可以认为就是father里面的内容全部加进son里面,所以son就可以访问father里面的内容。

 

 注释的内容就是son从father那里继承过来的内容,只是没有显示出来,但同样可以使用

 2.使用场景

当我需要定义两个类时,但恰好这两个类的内容有大部分是相同的。

class teacher
{
public:void teach(){cout << "I respond to teach" << endl;}void approach(){cout << " I can admitted" << endl;}
protected:int tall;int age;int rank;
};
class student
{
public:void study(){cout << "I respond to teach" << endl;}void approach(){cout << " I can admitted" << endl;}
protected:int tall;int age;int rank;
};

我们看以上代码有两个类,但这两个类里面的内容大部分是相同的,如果我们还要定义其它的类,也和这两个类大部分的内容是相同的,那么一个一个定义是不是有点太麻烦了。

那么这时候我们只需将相同的部分全部放入一个类A里面,其它的类只要定义自己特殊的那部分,然后再继承类A,那么就完成了我们需要的定义

代码如下

class person
{
public:void approach(){cout << " I can admitted" << endl;}
protected:int tall;int age;int rank;
};
class teacher:public person
{
public:void teach(){cout << "I respond to teach" << endl;cout << rank << endl;}void prove(){rank = 20;}
};
class student:public person
{
public:void study(){cout << "I respond to teach" << endl;cout << rank << endl;}void error(){rank = 10;}
};
int main()
{student xiaoming;teacher laoxu;laoxu.prove();xiaoming.error();laoxu.teach();
}

 3.继承规则

我们来看这一段,当我们用public的方式继承person的时候,变量DNA是不能访问的,但rank可以访问,因为DNA再person的private范围里

 2.特殊的继承方式

1.类模板继承

template<class T>
class Stack : public vector<T>
{
public:void PUSH(const T& x){vector<T>::push_back(x);}void pop(){vector<T>::pop_back();}T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}
};
int main()
{Stack<int> s1;s1.PUSH(1);s1.PUSH(2);s1.PUSH(3);s1.PUSH(4);s1.PUSH(5);s1.PUSH(6);s1.pop();cout << s1.top() << endl;cout << s1.empty() << endl;
}

上述代码中Stack直接继承了库函数中的vector,并且提供了模板参数,这样就大大方便了我们去模拟实现Stack 

2.基类和派生类之间的转换 

public继承的派生类对象可以赋值给基类的指针,形象地说就是,派生类将基类多出来的那部分切割掉,然后再赋值给基类的指针和引用

 

 如下图代码

class person
{
public:void study(){cout << " I can studying " << endl;}void teach(){cout << "I can teaching" << endl;}string name;int age;int sex;
};
class student:public person
{
public:int NO;
};
int main()
{student s1;s1.age = 10;person* p2;p2 = &s1;cout << p2->age << endl;
}

可以通过基类的指针访问派生类的成员 

基类不能赋值给派生类 

 3.隐藏规则

当派生类中有和基类一样的函数,那么派生类便会隐藏基类的函数

class person
{
public:void study(){cout << " I can studying " << endl;}void teach(){cout << "I can teaching" << endl;}
};
class student:public person
{
public:void study(){cout << " He can not to studying" << endl;}
};
int main()
{student s1;s1.study();
}

我们可以看到,当我们调用study函数时,并没有调用基类的study函数,而是调用了派生类的study函数,因为派生类中有和基类同名的函数,所以派生类便隐藏了基类的函数 

当我们需要调用基类的函数时我们需要特殊声明比如 

 

声明格式 基类名称 :函数 

函数隐藏只要是同名便会隐藏,所以不存在所谓重载 

 

并不建议将派生类函数和基类函数用作同一名字 

3.四种派生类的默认成员函数

1.默认构造函数

当我们调用派生类的默认构造函数时,同时也会调用基类的默认构造函数

class person
{
public:person(const char* ch = "xiaoming"){name = ch;cout << "until to person" << endl;}protected:const char* name;
};
class student:public person
{
public:student(){cout << "until to student" << endl;tall = 100;age = 10;}protected:int tall;int age;
};
int main()
{student s1;
}

我们看如上代码 和 结果

当我实例化派生类对象时,同时也会调用基类的默认构造函数,那是因为要用基类的默认构造函数去初始化基类对象。

 2.析构函数

class person
{
public:person(const char* ch = "xiaoming"){name = ch;cout << "until to person" << endl;}~person(){cout << " until to ~person" << endl;}
protected:const char* name;
};
class student:public person
{
public:student(){cout << "until to student" << endl;tall = 100;age = 10;}~student(){cout << "until to ~student" << endl;}
protected:int tall;int age;
};
int main()
{student s1;
}

如上图所示,当我调用派生类对象的析构函数时,我们会先调用派生类对象的析构函数,再调用基类的析构函数,这和默认构造函数的顺序有所不同,我们先初始化基类再初始化派生类,先析构派生类再析构基类 

 3.拷贝构造函数

class person
{
public:person(const char* ch = "xiaoming"){name = ch;cout << "until to person" << endl;}person(const person& ps){name = ps.name;}~person(){cout << " until to ~person" << endl;}
protected:const char* name;
};
class student:public person
{
public:student(){cout << "until to student" << endl;tall = 100;age = 10;}student(const student& st){tall = st.tall;age = st.age;}~student(){cout << "until to ~student" << endl;}void inital(){tall = 180;age = 20;name = "xiaozhang";}
protected:int tall;int age;
};
int main()
{student s1;s1.inital();student s2(s1);
}

如上述代码,我们将s1的数据更改,再用s1的数据去拷贝s2,那么s1和s2的结果会是怎么样呢 

 我们发现s1和s2位于基类中的成员变量不同,这是因为当调用拷贝构造时,并没有调用到基类的拷贝构造,所以导致基类成员的数据没有拷贝进去

我们需要在初始化列表里调用基类的拷贝构造函数

 

在派生类的拷贝构造 初始化列表中调用基类的拷贝构造

 4.重载赋值运算符

class person
{
public:person(const char* ch = "xiaoming"){name = ch;cout << "until to person" << endl;}person(const person& ps){cout << "COPY until to person" << endl;name = ps.name;}person& operator=(const person& st1){cout << "= person until to =" << endl;if (this != &st1){name = st1.name;}return *this;}~person(){cout << " until to ~person" << endl;}
protected:const char* name;
};
class student:public person
{
public:student(){cout << "until to student" << endl;tall = 100;age = 10;}student(const student& st):person(st){cout << "COPY until to student" << endl;tall = st.tall;age = st.age;}~student(){cout << "until to ~student" << endl;}student& operator=(const student& st1){cout << "= person until to =" << endl;if (this!=&st1){person::operator=(st1);tall = st1.tall;age = st1.age;}return *this;}void inital(){tall = 180;age = 20;name = "xiaozhang";}
protected:int tall;int age;
};
int main()
{student s1;s1.inital();student s2;s2 = s1;
}

通过实现这最后一个函数我们可以看看成员函数的调用规则 

 

首先构造两个派生类,分别调用两次基类构造和派生类构造

然后赋值再调用两次赋值函数

析构先析构派生类再析构基类 

4.不能被继承的类

第一种

第一种在基类定义时 名称后面加final 

当我们定义基类时,在后面加final ,那么派生类在继承时就失败了 

第二种 

第二种,将默认构造函数放在private范围内 

 

当我们将基类的默认构造函数放在private里时,派生类在构造对象时会调用基类的默认构造函数,但是因为基类的默认构造函数是存在于private内的,所以会调用失败 

5.继承与友元 

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

using namespace std;
#include<iostream>
class person
{friend void print();
public:person(){cout << "I am a person" << endl;}
private:int gender=0;int tall=0;
};
class student :public person
{
public:student(){cout << "I am a student" << endl;}
private:int grade=0;
};
void print()
{person p1;p1.gender = 10;cout << p1.gender << endl;//print()是基类的友元函数,所以可以访问基类构成的对象private成员student s1;s1.grade = 10;cout << s1.grade << endl;//虽然student继承了person,但友元关系不能继承,所以print()不能访问派生类的private成员
}
int main()
{print();
}

 6.继承与静态成员

基类定义了一个static成员,则整个继承过程中,只会产生一个static,不管继承多少次,都只会有这一个static成员。

using namespace std;
#include<iostream>
class person
{
public:static int st;
private:int gender=0;int tall=0;
};
int person::st = 50;
//类中的静态变量要在类外初始化
class student :public person
{
public:
private:int grade=0;
};
int main()
{student s1;person p1;cout << p1.st << endl;//输出50cout << s1.st << endl;//输出50//不管继承多少次它们的st都是同一个st,因为st存储在静态区中
}

 7.继承模型

1.单继承

 上述类似单继承模型一个基类只被一个派生类继承一次,并且每个派生类都只被一个派生类继承一次

2.多继承 

以上就是多继承模型指的是一个基类或一个派生类被两个或两个以上的派生类所继承 

3.菱形继承 

以上就是菱形继承的模型,指的就是一个派生类对另一个派生类或者基类继承了两次或两次以上 

但是这样的话就会有双份数据,所以会使对象在调用的时候不明确调用那个 

using namespace std;
#include<iostream>
class person
{
public:int gender=0;int tall=0;
};
class student :public person
{
public:
private:int grade=0;
};
class classmate :public person
{
public:
private:int next;
};
class myself : public classmate, public student
{
public:
private:int myself;
};
int main()
{myself MY;cout << MY.gender << endl;//调用gender失败,编译器不知道该调用哪个gendercout << MY.classmate::gender << endl;//调用成功我们需要指出该调用的类范围
}

C++为了解决这样的问题,发明了虚继承 

4.虚继承 

 

当有一个基类被继承两次时我们需要在第一次被继承时加上virtual进行虚继承 

比如 

通过增加virtual来对person进行虚继承,那么这样myself继承时就只会继承一份person 

但是并不建议设计菱形模型,因为这样会复杂非常多 

 5.多继承指针偏移问题

using namespace std;
#include<iostream>
class student
{
public:
private:int grade=0;
};
class classmate
{
public:
private:int next=0;
};
class myself : public classmate, public student
{
public:
private:int myself;
};
int main()
{myself MY;classmate* p1 = &MY;myself* p2 = &MY;student* p3 = &MY;cout <<" classmate : " << p1 << endl;cout <<" myself    : " << p2 << endl;cout <<" student   : " << p3 << endl;}

我们来看上述代码 

myself先是继承了classmate,然后再继承了student那么myself的顺序空间如下 

 

classmate在前student在后 

当我们使用myself类型指针和classmate类型指针和student类型指针指向这个myself对象时指针位置如图所示 

所以classmate和myself在同一位置而student在它们后面。所以代码运行结果如下 

 

 8.继承和组合

1.组合的概念

继承我们已经讲过了,那我们来讲讲组合的方式

 

这便是组合的方式,我们直接在一个类里面构建另外一个类的对象,我们可以使用这个对象的函数。  

2.组合和继承的优缺点 

组合将被组合的内部结构封闭了起来,使我们自己构建的类不能访问它的private和protected,安全性比较高,但继承不一样,继承可以访问,所以安全性比较低,我们能使用组合时尽量使用组合。

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

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

相关文章

【机器学习】均方误差根(RMSE:Root Mean Squared Error)

均方误差根&#xff08;Root Mean Squared Error&#xff0c;RMSE&#xff09;是机器学习和统计学中常用的误差度量指标&#xff0c;用于评估预测值与真实值之间的差异。它通常用于回归模型的评价&#xff0c;以衡量模型的预测精度。 RMSE的定义与公式 给定预测值 和实际值 …

Pandas | 数据分析时将特定列转换为数字类型 float64 或 int64的方法

类型转换 传统方法astype使用value_counts统计通过apply替换并使用astype转换 pd.to_numericx对连续变量进行转化⭐参数&#xff1a;返回值&#xff1a;示例代码&#xff1a; isnull不会检查空字符串 数据准备 有一组数据信息如下&#xff0c;其中主要将TotalCharges、MonthlyC…

混沌工程遇上AI:智能化系统韧性测试的前沿实践

#作者&#xff1a;曹付江 文章目录 1、什么是AI驱动的混沌工程&#xff1f;2、AI与混沌工程结合的价值3、技术实现3.1 AI模型开发3.1.1模型选择与构建3.1.2模型训练3.1.3 模型验证与调参3.1.4 模型测试3.1.5 知识库建设与持续学习 4、混沌工程与AI实践结合4.1 利用AI从运维专家…

《深度学习神经网络:颠覆生活的魔法科技与未来发展新航向》

深度学习神经网络对我们生活的影响 一、医疗领域 深度学习神经网络在医疗领域的应用可谓意义重大。在疾病诊断方面&#xff0c;它能够精准分析医疗影像&#xff0c;如通过对大量的 CT、MRI 图像进行深度学习&#xff0c;快速准确地识别出微小的肿瘤病变&#xff0c;为医生提供…

YOLOv11融合特征细化前馈网络 FRFN[CVPR2024]及相关改进思路

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 一、 模块介绍 论文链接&#xff1a;Adapt or Rerish 代码链接&#xff1a;https://github.com/joshyZhou/AST 论文速览&#xff1a;基于 transformer 的方法在图像恢复任务中取得了有希望的性能&#xff0c;因为…

K8S简单部署,以及UI界面配置

准备两台服务器K8Smaster和K8Sminion 分别在两台服务器上执行以下代码 #添加hosts解析&#xff1b; cat >/etc/hosts<<EOF 127.0.0.1 localhost localhost.localdomain 192.168.45.133 master1 192.168.45.135 node2 EOF #临时关闭selinux和防火墙&#xff1b; sed …

爬虫 - 二手交易电商平台数据采集 (一)

背景: 近期有一个需求需要采集某电商网站平台的商品数据进行分析。因此&#xff0c;我计划先用Python实现一个简单的版本&#xff0c;以快速测试技术的实现可能性&#xff0c;再用PHP实现一个更完整的版本。文章中涉及的技术仅为学习和测试用途&#xff0c;请勿用于商业或非法用…

Chrome与傲游浏览器性能与功能的深度对比

在当今数字化时代&#xff0c;浏览器作为我们日常上网冲浪、工作学习的重要工具&#xff0c;其性能与功能直接影响着我们的使用体验。本文将对Chrome和傲游两款主流浏览器进行深度对比&#xff0c;帮助用户更好地了解它们的差异&#xff0c;以便做出更合适的选择。&#xff08;…

大华乐橙设备私有平台EasyCVR视频设备轨迹回放平台支持哪些摄像机?摄像机如何选型?

在现代安全监控系统中&#xff0c;视频监控设备扮演着至关重要的角色。视频设备轨迹回放平台EasyCVR以其卓越的兼容性和灵活性&#xff0c;支持接入多种品牌和类型的摄像机。这不仅为用户提供了广泛的选择空间&#xff0c;也使得视频监控系统的构建和管理变得更加高效和便捷。本…

数据结构 栈和队列

目录 1. 栈1.1 栈的概念及结构1.2 栈的实现 2. 队列2.1 队列的概念及结构2.2 队列的实现 正文开始 1. 栈 1.1 栈的概念及结构 栈是线性表的一种&#xff0c;这种数据结构只允许在固定的一端进行插入和删除元素的操作&#xff0c;进行数据插入和删除的一端称为栈顶&#xff0c…

Cross Modal Transformer: Towards Fast and Robust 3D Object Detection

代码地址 https://github.com/junjie18/CMT 1. 引言 在本文中&#xff0c;我们提出了Cross-Modal Transformer&#xff08;CMT&#xff09;&#xff0c;这是一种简单而有效的端到端管道&#xff0c;用于鲁棒的3D对象检测&#xff08;见图1&#xff08;c&#xff09;&#xf…

深度学习鲁棒性、公平性和泛化性的联系

深度学习鲁棒性、公平性和泛化性的联系 前言1 鲁棒性、公平性、泛化性本质2 对抗攻击是混杂效应3 因果推理角度3.1 稳定学习 VS 公平性3.2 后门攻击 前言 读研好不容易从边缘智能&#xff0c;费好大劲被允许转到联邦学习赛道&#xff0c;再费了好大劲和机缘巧合被允许转到可信A…

【Vue】简易博客项目跟做

项目框架搭建 1.使用vue create快速搭建vue项目 2.使用VC Code打开新生成的项目 端口号简单配置 修改vue.config.js文件&#xff0c;内容修改如下 所需库安装 npm install vue-resource --save --no-fund npm install vue-router3 --save --no-fund npm install axios --save …

python爬虫指南——初学者避坑篇

目录 Python爬虫初学者学习指南一、学习方向二、Python爬虫知识点总结三、具体知识点详解和实现步骤1. HTTP请求和HTML解析2. 正则表达式提取数据3. 动态内容爬取4. 数据存储5. 反爬虫应对措施 四、完整案例&#xff1a;爬取京东商品信息1. 导入库和设置基本信息2. 获取网页内容…

一文学习Android中的Treeview

在Android开发中&#xff0c;TreeView是一种用于显示层次结构的组件&#xff0c;可以让用户展开和折叠子项&#xff0c;以方便查看数据的不同层次。TreeView在文件系统、组织架构、目录结构等场景中非常有用&#xff0c;尽管Android并未提供内置的TreeView控件&#xff0c;但可…

如何快速搭建一个spring boot项目

一、准备工作 1.1 安装JDK&#xff1a;确保计算机上已安装Java Development Kit (JDK) 8或更高版本、并配置了环境变量 1.2 安装Maven&#xff1a;下载并安装Maven构建工具&#xff0c;这是Spring Boot官方推荐的构建工具。 1.3 安装代码编辑器&#xff1a;这里推荐使用Inte…

SLM401A系列42V商业照明线性恒流芯片 线性照明调光在LED模组及灯带智能球泡灯上应用

SLM401A系列型号选型&#xff1a; SLM401A10ED-7G:QFN1010-4 SLM401A15aa-7G:SOT23-3 SLM401A20aa-7G:SOT23-3 SLM401A20ED-7G:QFN1010-4 SLM401A25aa-7G:SOT23-3 SLM401A30aa-7G:SOT23-3 SLM401A40aa-7G:SOT23-3 SLM401A50aa-7G:SOT23-3 SLM401A6…

HTB:GreenHorn[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 再次使用nmap对这三个端口进行脚本、服务扫描 尝试先通过curl访问靶机80端口 将靶机IP与该域名写入hosts使DNS本地解析 使用浏览器访问greenhorn.htb 使用Wappalyzer插件查看该页面技术栈 尝试在sea…

HarmonyOS-消息推送

一. 服务简述 Push Kit&#xff08;推送服务&#xff09;是华为提供的消息推送平台&#xff0c;建立了从云端到终端的消息推送通道。所有HarmonyOS 应用可通过集成 Push Kit&#xff0c;实现向应用实时推送消息&#xff0c;使消息易见&#xff0c;构筑良好的用户关系&#xff0…

精选 Top10 开源调度工具,解锁高效工作负裁自动化

在大数据和现代 IT 环境中&#xff0c;任务调度与工作负载自动化&#xff08;WLA&#xff09;工具是优化资源利用、提升生产效率的核心驱动力。随着企业对数据分析、实时处理和多地域任务调度需求的增加&#xff0c;这些工具成为关键技术。 本文将介绍当前技术发展背景下的Top …