C++——继承

在这里插入图片描述

文章目录

  • 🦜1. 什么是继承
    • 🐊1.1 概念
    • 🐊1.2 格式
    • 🐊1.3 继承方式 & 访问限定符
  • 🐦2. 派生类和基类的赋值问题
  • 🦩3. 派生类和基类同名成员问题
  • 🐓4.派生类默认成员函数
    • 🐉4.1 构造函数
    • 🐉4.2 拷贝构造
    • 🐉4.3 赋值运算符重载
    • 🐉4.4 析构函数
  • 🐥5. 友元和静态成员
  • 🐧6. 多继承

🦜1. 什么是继承

🐊1.1 概念

在现实生活中,谈起继承,就会联想到继承家业、家产。

而在编程世界中,继承也是如此,一个类(称子类或者派生类),可以继承另一个类(称父类基类)的属性和行为。

//定义一个人的属性	基类
class Person
{
public:Person(string name = "Kangkang", string gender = "male", int age = 18):_name(name),_gender(gender),_age(age){cout << "Person()" << endl;}void Print(){cout << "name:" << _name << endl;cout << "gender:" << _gender << endl;cout << "age:" << _age << endl;}
protected:string _name;	//	姓名string _gender;	//	姓别int _age;	//年龄
};
//定义一个学生类,继承人的属性		子类
class Student :public Person
{
public:Student(string name = "Lihua", string gender = "female", int age = 20, int id=111):Person(name,gender,age),_stuId(id){};
protected:int _stuId;	//学号
};
int main()
{Person p;Student stu("Lisa","female",20,20230812);p.Print();stu.Print();return 0;
}

🐊1.2 格式

class 子类 : 继承方式 基类
{};

image-20230813211527815

🐊1.3 继承方式 & 访问限定符

image-20230813212440542

继承方式public继承protected继承private继承
父类public成员子类的public成员子类的protected成员子类的private成员
父类的protected成员子类的protected成员子类的protected成员子类的private成员
父类的private成员子类不可见子类不可见子类不可见

这里其实很好分辨,我们只需要取权限小的即可:public>protected>private

对于public成员,我们可以直接在类的外面访问调用,而对于protected成员,可在类里面通过this指针访问,而private成员,虽然继承到了派生类对象中,但无法访问到,也可以理解为将父类的成员设为private就是不想让其他类继承

image-20230813220139015

但是在实际应用过程中,一般都是采用的public继承方式

Tips:

关键字class不指定继承方式时,默认继承方式为private

而使用struct关键字时,默认继承方式为public

但这里还是建议,每次都显示继承方式

class A
{
public:void func1() { cout << "func1()" << endl; }
protected:void func2(){ cout << "func2()" << endl; }
private:void func3(){ cout << "func3()" << endl; }int _a = 0;
};
class B :public A
{
public:void Print(){this->func1();this->func2();}int _b = 1;};
int main()
{B b;b.Print();
}

🐦2. 派生类和基类的赋值问题

派生类和基类之间的赋值操作涉及到对象切片的问题。派生类的对象可以赋值给基类对象/基类指针/基类引用 ,但反过来(将基类对象赋值给派生类对象)是不合法的,因为这可能导致对象切片,即派生类对象的额外成员信息丢失

image-20230813222919821

这就好比,学习C++,C++是在C语言的基础上衍生出来的,可以理解问C++继承了C语言的衣钵,C++的代码可以兼容C的代码;反之,C的代码却不能却不能兼容C++。

image-20230813223945086

🦩3. 派生类和基类同名成员问题

class A
{
public:int _x=1;int _y=2;void Print(){cout << "A()" << endl;}
};
class B :public A
{
public:int _x = 3;int _y = 4;void Print(){cout << "B()" << endl;}
};
int main()
{B b;cout << b._x << endl;	// 3cout << b._y << endl;	// 4b.Print();	// B()
}

这段代码基类A和派生类B,成员名都是相同的,但我们输出发现,输出的是派生类的成员,那这里是否继承了A的这些成员呢?

image-20230813225150530

通过监视窗口发现,这里A是被B继承了,但是由于成员名相同,A被B给隐藏了,这也叫重定义

如果要访问基类的成员,可使用基类:基类成员显示访问,这也可理解问他们都有着独立的作用域

image-20230813225536058

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

🐉4.1 构造函数

派生类的构造函数必须调用基类的构造函数来初始化继承下来的那部分成员;如果基类没有默认构造,那在派生类构造函数的初始化列表显示调用

class Person
{
public://全缺省,默认构造Person(string name = "Kangkang"):_name(name){}
protected:string _name;
};
class Student : public Person
{
public:Student(string name, int id):Person(name),_id(id){}void Print(){cout << "name:" << _name << endl;cout << "id:" << _id << endl;}
protected:int _id;
};
int main()
{Student stu("Lisa",2023);stu.Print();return 0;
}

🐉4.2 拷贝构造

派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化,但我们可以直接传子类对象,因为调用父类的拷贝构造时,父类会自动切片拿到父类中的对象

父类拷贝构造

Person(const Person& p):_name(p._name)
{}

子类拷贝构造

Student(const Student&stu):Person(stu._name),_id(stu._id)
{}

调用

Student stu("Lisa", 2023);
stu.Print();
Student stu2(stu);
stu2.Print();

🐉4.3 赋值运算符重载

子类的operator=必须要调用父类的operator=完成基类的复制;但是因为赋值运算符重载了=,那么子类和父类的名字都是一样,这样就造成了子类隐藏了父类的operator=。所以需要显示调用父类的operator=

//operator=
Person& operator=(const Person& p)
{if (this != &p){_name = p._name;}return *this;
}
Student& operator=(Student& stu)
{if (this != &stu){//指定调用父类Person::operator =(stu);_id = stu._id;}return *this;
}

🐉4.4 析构函数

子类的析构函数会在被调用完成后自动调用父类的析构函数清理基类成员;因为这样才能保证子类对象先清理子类成员再清理父类成员的顺序。

Tips:

切记,这里是自动调用父类的析构,所以我们不需要在子类的析构函数中调用父类的析构函数

如果这里有指针,同一块区域释放两次,会造成未定义行为

🐥5. 友元和静态成员

在继承中,友元关系是不可以被继承的,就好比咱们朋友的朋友,不一定是咱们的朋友。

对于静态成员,这里继承的是它的使用权,就比如家里有三个孩子,一个大哥哥,两个小弟弟,这个哥哥是他两“共用的”,并不会说2个弟弟必须有2个哥哥。

class A
{
public:static int _sa;int _a;
};
int A::_sa = 1;
class B :public A
{
public:int _b;
};int main()
{A a;B b;cout <<"a._a:" << &a._a << endl;cout <<"b._a:" << &b._a << endl;cout <<"a._sa:" << &A::_sa << endl;cout <<"b._sa:" << &B::_sa<< endl;
}

这里也可以验证,对于静态成员,父类和子类是共用的(可用于计算父类有多少个派生类)。

🐧6. 多继承

对于一个子类只有一个直接父类,这种关系称为单继承

//单继承
class A
{};
class B:public A
{};
class C :public B
{};

而对于一个子类有多个直接父类,这种关系称为多继承

//多继承
class A
{};
class B
{};
class C :public A, public B
{};

多继承会引发一个很麻烦的问题——菱形继承

image-20230814002934232

我们先来上代码

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

这段代码,直接报错,_a的指定不明确,因为D类继承了B类和C类,编译器不知道这个_a是属于继承的哪个类,从而产生二义性的问题。

image-20230814003431003

当然,前面也提到过,可以通过指定类域,来明确告诉编译器,这属于哪个类

D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;

这样虽然解决了二义性的问题,但是这样看的数据十分冗余,很容易分不清哪个是哪个

image-20230814004447944

为了填补这个坑,推出了一种名为虚拟继承的继承方式(仅限菱形继承使用,其他地方不要使用)

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;d._a = 6;return 0;
}

使用虚拟继承之后,我们发现这里的_a,只有一份了,而且我们查看内存发现,数据并不是连在一起,多了一些地址

image-20230814011531088

这叫做虚基表,用来寻找基类偏移量的表,虚拟继承的派生类里面就包含了这个虚表,这个虚表记录着距离基类的偏移量,如果要用到基类的数据,加上这个距离就能找到,这样就解决了数据的二义性和数据冗余的问题。

但是在实际过程中,这个模型十分鸡肋且复杂,所以一般都不会采用这种继承方式。


多继承就属于C++语法复杂的一个体现,而继承虽然可以复用,但是继承的耦合度十分高,代码直接的依赖关系很强,这样就造成了代码的不便于维护。但又涉及到多态必须使用继承,所以在实际之中,代码要复用的话,我们得分场景。
那本期的方向就到这咯,我们下期再见,如果有下期的话。

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

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

相关文章

React源码解析18(1)------ React.createElement 和 jsx

1.React.createElement 我们知道在React17版本之前&#xff0c;我们在项目中是一定需要引入react的。 import React from “react” 即便我们有时候没有使用到React&#xff0c;也需要引入。原因是什么呢&#xff1f; 在React项目中&#xff0c;如果我们使用了模板语法JSX&am…

使用OkHttp发送POST请求的几种方式

使用OkHttp发送POST请求的几种方式 介绍pom依赖基本的POST请求带授权的POST请求POST方式发送JSON数据Multipart POST 请求 介绍 本文将介绍 OkHttp 客户端的基本用法。 主要介绍 OkHttp 3.x 版本中发送Post请求的几种方式。 pom依赖 <dependency><groupId>com.sq…

单调递增的数字——力扣738

文章目录 题目描述解法题目描述 解法 #include<iostream> #include<string>using namespace std;int monotoneIncreasingDigits

【学习】若依源码(前后端分离版)之 “ 异常处理”

大型纪录片&#xff1a;学习若依源码&#xff08;前后端分离版&#xff09;之 “ 异常处理” 前言1、统一返回实体定义2、定义登录异常定义3、基于ControllerAdvice注解的Controller层的全局异常统一处理4、测试访问请求结语 前言 通常一个web框架中&#xff0c;有大量需要处理…

中小企业项目管理软件推荐:选择适合的工具提升项目效率!

中小企业项目管理软件有哪些&#xff1f;Zoho Projects是一款好用无广告的项目管理软件。当个小创业者是真的不容易&#xff0c;不仅要管理团队&#xff0c;还要管理团队项目。很多团队之前用了好多项目管理的软件&#xff0c;但是都不太满意。但是如果你经常参加创业者聚会上&…

常见的路由协议之RIP协议与OSPF协议

目录 RIP OSPF 洪泛和广播的区别 路由协议是用于在网络中确定最佳路径的一组规则。它们主要用于在路由器之间交换路由信息&#xff0c;以便找到从源到目标的最佳路径。 常见的路由协议&#xff1a; RIP (Routing Information Protocol)&#xff1a;RIP 是一种基于距离向量算…

Mac os 上的apt-get install 就是brew install

Mac os 上面不支持apt-get install ,但是有个 brew install可以代替。 Homebrew是Mac OS的包管理器&#xff0c;可以方便地安装各种需要的软件。 1.1 安装Homebrew 如果没有安装Homebrew&#xff0c;需要在终端输入以下命令进行安装&#xff1a; /usr/bin/ruby -e "$(…

使用wxPython和PyMuPDF在Python中显示PDF目录的实现

展示如何使用wxPython和PyMuPDF库在Python中选择PDF文件并将目录显示在列表框中。 简介&#xff1a; 在本篇教程中&#xff0c;我们将学习如何使用wxPython和PyMuPDF库在Python中选择PDF文件&#xff0c;并将其目录显示在一个列表框中。这将使用户能够方便地浏览PDF文档的目录…

c#实现设配器模式

下面是一个使用C#实现适配器模式的示例代码&#xff1a; using System;// 目标接口 public interface ITarget {void Request(); }// 目标类 public class Target : ITarget {public void Request(){Console.WriteLine("目标类的请求");} }// 需要适配的类 public c…

Golang 局部变量、全局变量 声明

文章目录 一、局部变量二、全局变量 一、局部变量 四种声明方式 多变量声明&#xff1a; package mainimport "fmt"//局部变量声明 func main() {//方法一: 声明一个变量和数据类型&#xff0c;不初始化值&#xff1b;默认值为0&#xff1b;var lvA intfmt.Printl…

【MybatisPlus】LambdaQueryWrapper和QueryWapper的区别

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

可视化应用:提升教育领域的学习与理解

在教育领域&#xff0c;可视化应用作为一种强大的工具&#xff0c;已经开始发挥着重要的作用。通过将抽象的概念和复杂的数据转化为直观的图形和图表&#xff0c;可视化应用能够提升学生的学习效果和理解能力。本文将探讨可视化应用在教育领域中的重要性&#xff0c;以及它在不…

电路基础之电容

电容器&#xff08;Capacitor&#xff09;是由两个导体电极之间夹着一个电介质而组成的元件。这两个电极可以是金属板、箔片、涂层等&#xff0c;而电介质则是放置在电极之间的绝缘材料。电容器的基本构成包括以下几个要素&#xff1a; 电极&#xff1a;电容器的电极是两个导体…

Ubuntu系统kubeadm安装K8S_v1.25.x容器使用docker(K8S_v1.24版本以后依然使用docker容器管理)

安装所需要的全部文档请点击这里下载 系统是&#xff1a; rootk8s-master:~# cat /etc/lsb-release DISTRIB_IDUbuntu DISTRIB_RELEASE22.04 DISTRIB_CODENAMEjammy DISTRIB_DESCRIPTION“Ubuntu 22.04.3 LTS” rootk8s-master:~# uname -a Linux k8s-master 5.15.0-76-generi…

js合并数组对象(将数组中具有相同属性对象合并到一起,组成一个新的数组)

一、根据数组对象中某一key值&#xff0c;合并相同key值的字段&#xff0c;到同一个数组对象中&#xff0c;组成新的数组 1.原数组&#xff1a; var array [{ id: 1, name: Alice },{ id: 2, name: Bob },{ id: 1, age: 25 },{ id: 3, name: Charlie, age: 30 } ];2.合并后数…

C++隐式调用和explicit关键字

隐式类型转换 #include <iostream> using namespace std;class Point { public:int x, y;Point(int x 0, int y 0): x(x), y(y) {} };void displayPoint(const Point& p) {cout << "(" << p.x << "," << p.y <&l…

接口测试实战,Jmeter正则提取响应数据-详细整理,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在测试时&#xf…

服务器安装JDK

三种方法 方法一&#xff1a; 方法二&#xff1a; 首先登录到Oracle官网下载JDK JDK上传到服务器中&#xff0c;记住文件上传的位置是在哪里&#xff08;我放的位置在/www/java&#xff09;&#xff0c;然后看下面指示进行安装 方法三&#xff1a; 首先登录到Oracle官网下载…

Skywalking-9.6.0系列之本地源码编译并启动

Skywalking相信有很多人使用过&#xff0c;通过容器或者下载安装包进行安装的&#xff0c;今天从源代码角度&#xff0c;拉取、构建、启动。 官方文档步骤简洁明了&#xff0c;我这边会结合自己遇到的一些问题做出总结。 当前构建资源版本&#xff1a; MAC 10.15.7IDEA 2021.…

基于STM32CUBEMX驱动TMOS模块STHS34PF80(1)----获取ID

基于STM32CUBEMX驱动TMOS模块STHS34PF80----1.获取ID 概述样品申请视频教程所有功能接口最小系统图生成STM32CUBEMX串口配置IIC配置IO口设置串口重定向 模块地址参考demoIIC写函数IIC读函数参考程序初始化获取ID主函数 概述 STHS34PF80 是一款非冷却、工厂校准的红外运动和存在…