[C++]:15.继承

继承

  • 一.继承:
    • 1.继承的概念和基本操作:
      • 1.概念:
      • 2.基本操作:
    • 2.继承格式和多种继承方法:
      • 1.基本继承格式:
      • 2.继承关系+访问限定符
    • 3.子类对象和父类对象之间的赋值:
      • 1.为什么存在赋值兼容转换?
      • 2.子类对象赋值给父类对象(赋值兼容转换):
        • 2-1:对象赋值:
        • 2-2:对象指针赋值:
        • 2-3:对象引用赋值:
      • 3.总结:
    • 4.继承中的作用域:
      • 1.问题:子类和父类可以有同名成员?
        • 1-1:同名成员:
        • 1-2:同名函数:
      • 2.问题:子类和父类成员函数不是重载关系?
      • 3.总结:
    • 5.子类的默认成员函数!
      • 1.构造函数:
      • 2.析构函数:
      • 3.拷贝:
      • 4.赋值:
    • 7.继承和友元+继承和静态:
      • 1.继承和友元:
      • 2.继承和静态:
    • 8.多继承:
      • 1.菱形继承:特殊的多继承
      • 2.1.菱形虚拟继承:
    • 9.继承的总结和反思:
      • 1.多继承
      • 2组合和继承:
      • 3.继承的作用:

一.继承:

1.继承的概念和基本操作:

1.概念:

继承就是一种类的复用,我们以前写的代码比较多的都是在主函数中使用各种函数这是函数的复用,在接触类和对象以后需要学习类的复用也就是继承。继承是面向对象的,这样的语法可以让程序员在原来的类的基础上继承产生一个新的的类,这个新的的类有被继承类的成员函数和成员变量。

2.基本操作:

两个类一个老师类,一个学生类,提取两个类中共有的内容去反向定义一个类person类让老师类和学生类都去继承人这个类实现类的复用:我们先去写一个关于学生的复用。

	class preson {public:preson(string name="xxxx", string six="xxx", int age=18):_name(name),_six(six),_age(age){}void information_person(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}protected:string _name;string _six;int _age;};class student : public preson {public:student(int score,int grade):_score(score),_grade(grade){}void information_student(){//1.被继承的成员变量可以直接使用:cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;//2.间接使用:information_person();//3.学生部分:cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;}protected:int _score;int _grade;};int main(){sfpy::student s1(88, 1);s1.information_student();return 0;}

在这里插入图片描述

观察代码和运行结果,我们发现被继承的这个类当前的成员变量和成员函数我们都可以去使用,这个地方没有办法去指定学生的person类中的成员内容?

2.继承格式和多种继承方法:

1.基本继承格式:

在这里插入图片描述

2.继承关系+访问限定符

1.继承方式:public继承 ,protected继承 , private继承。
2.访问方式:public访问 ,protected访问 , private访问。
4.继承方式+访问方式才可以确定子类对父类的成员的使用情况!
5.注意:
5-1:不去显示的去写继承方式,class来说默认private继承 , struct是公有继承。
5-2:建议:public继承方式 + 成员变量使用保护 + 成员方法使用公有。
*
总结:父类的 A 成员 和 子类的B继承组合在一起,对于子类来说对于子类来说这个父类的成员在子类中有着 A&&B中较小的一个情况。
*
public > protected > private;
保护就是因为继承才产生的,公有+保护 保护+公有 保护+保护 对于子类来说都可以去使用父类的成员!
在这里插入图片描述

3.子类对象和父类对象之间的赋值:

1.为什么存在赋值兼容转换?

不同类型之间在进行 赋值,大小比较,的时候都会产生临时变量。通过临时变量进行操作。下面这个地方通过a的值产生了一个float类型的临时变量然后临时变量和b进行比较在这个地方。

int main()
{int a = 10;float b = 20.5;if (a > b){cout << "a>b" << endl;}else{cout << "a<b" << endl;}return 0;
}

子类赋值个父类的过程中是不存在中间变量的?
回答:临时变量具有常性不能把具有const类型的数据赋值给person对象。
解决办法:特殊处理—>赋值兼容转换—>中间不产生临时变量,做了一个切片处理。

2.子类对象赋值给父类对象(赋值兼容转换):

子类的对象---->赋值给---->父类对象
子类的对象指针---->赋值给---->父类对象指针
子类的对象引用---->赋值给---->父类对象引用

int main()
{sfpy::student s1(88, 1);//1.切片赋值--->产生新的切片:sfpy::preson p1 = s1;//2.切片赋值sfpy::preson* p2 = &s1;//3.切片赋值sfpy::preson& p3 = s1;return 0;
}
2-1:对象赋值:

在这里插入图片描述

2-2:对象指针赋值:

在这里插入图片描述

2-3:对象引用赋值:

在这里插入图片描述

3.总结:

在这里插入图片描述

4.继承中的作用域:

1.问题:子类和父类可以有同名成员?

回答:子类和父类属于不同的作用域可以有同名成员!

1-1:同名成员:
class preson {public:preson(string name = "xxxx", string six = "xxx", int age = 18):_name(name), _six(six), _age(age){}void information(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}protected:string _name;string _six;int _age;};class student : public preson {public:student(string name ,int score, int grade):_name(name),_score(score), _grade(grade){}void information(){//1.被继承的成员变量可以直接使用:指定类域cout << "名字:" << preson::_name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;cout << "名字:" << _name << endl;cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;}protected:string _name;int _score;int _grade;};

在这里插入图片描述

1.成员变量的一个同名成员,子类优先访问自己的成员变量,如果在这样的情况下想要去访问父类的成员变量需要指定类域去进行访问。
2.父子类的同名成员变量,构成隐藏关系。

1-2:同名函数:
namespace sfpy {class preson {public:preson(string name="xxxx", string six="xxx", int age=18):_name(name),_six(six),_age(age){}void information(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}protected:string _name;string _six;int _age;};class student : public preson {public:student(int score,int grade):_score(score),_grade(grade){}void information(){//1.被继承的成员变量可以直接使用:cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;2.间接使用:指定类域person::information();//3.学生部分:cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;}protected:int _score;int _grade;};}

1.成员变量的一个同名成员,子类优先访问自己的成员变量,如果在这样的情况下想要去访问父类的成员函数需要指定类域去进行访问。
2.父子类的同名成员函数,构成隐藏关系,不需要考虑返回值和参数的情况!

2.问题:子类和父类成员函数不是重载关系?

回答:构成重载需要成员函数在同一作用域下!

3.总结:

1.在实际的情况当中不需要去定义同名的成员!
2.注意函数重载和隐藏关系的不同。

5.子类的默认成员函数!

子类和父类的这些默认成员函数进行分别调用的!
1.对子类做处理就去掉用子类
2.对父类做处理就去掉用父类

1.构造函数:

1.不去使用父类的默认构造通过显示的去调用父类的构造可以通过子类传参的方法对父类的成员进行初始化构造。
2.父类有默认构造函数那么如果我们不去显示的调用构造那么走默认构造(无参,全缺省,列表初始化)

	class preson {public:preson(string name = "xxxx", string six = "xxx", int age = 18):_name(name), _six(six), _age(age){}void information_person(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}protected:string _name;string _six;int _age;};class student : public preson {public:student(string name,string six, int age,int score, int grade):preson(name,six,age), _score(score), _grade(grade){}void information_student(){//1.被继承的成员函数可以直接使用:information_person();cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;cout << endl;}protected:int _score;int _grade;};

在这里插入图片描述

2.析构函数:

在这里插入图片描述

为什么我们在子类的析构函数中显示的去掉用父类的析构函数会报错?
1.有父子关系的两个类的析构函数名称会被统一转化为distruct。
2.两个类的析构函数就构成了隐藏的关系。
3.同名称的函数需要指定类域。

在这里插入图片描述

我们如果去显示的去析构对象,先析构子的部分还是父的部分?
1.我们应该先析构子节点再去析构父节点。
2.因为如果存在先析构父节点那么如果子节点的析构操作需要父节点的数值怎么办?
3.为了保证先去调用子节点后调用父节点子类的析构函数不需要显示的去调用父类的析构函数。编译器会在调用完子类的析构然后自动的去调用父类的析构

3.拷贝:

1.编译器默认生成的 拷贝构造会去完成值拷贝。
2.对于当前的例子来说完成值拷贝是没有问题的。
3.显示的去调用父类的拷贝构造子类的初始化列表中去使用。
4.我们知道在一些情况下有可能发生浅拷贝的问题是需要进行显示调用。

class preson {
public:preson(string name = "xxxx", string six = "xxx", int age = 18):_name(name), _six(six), _age(age){}preson(const preson& p1){_name = p1._name;_six = p1._six;_age = p1._age;}void information_person(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}~preson(){cout << "~preson" << endl;}
protected:string _name;string _six;int _age;
};class student : public preson {
public:student(string name, string six, int age, int score, int grade):preson(name, six, age), _score(score), _grade(grade){}student(const student& s1):preson(s1),_score(s1._score),_grade(s1._grade){}void information_student(){//1.被继承的成员函数可以直接使用:information_person();cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;cout << endl;}~student(){preson::~preson();cout << "~student" << endl;}
protected:int _score;int _grade;
};}

4.赋值:

class preson {
public:preson(string name = "xxxx", string six = "xxx", int age = 18):_name(name), _six(six), _age(age){}preson(const preson& p1){_name = p1._name;_six = p1._six;_age = p1._age;}void information_person(){cout << "名字:" << _name << endl;cout << "性别:" << _six << endl;cout << "年龄:" << _age << endl;}preson& operator=(const preson& p){//1.不能自己给自己进行赋值:if (this != &p){_name = p._name;_six = p._six;_age = p._age;}return *this;}~preson(){cout << "~preson" << endl;}
protected:string _name;string _six;int _age;
};class student : public preson {
public:student(string name, string six, int age, int score, int grade):preson(name, six, age), _score(score), _grade(grade){}student(const student& s1):preson(s1),_score(s1._score),_grade(s1._grade){}void information_student(){//1.被继承的成员函数可以直接使用:information_person();cout << "成绩:" << _score << endl;cout << "年级:" << _grade << endl;cout << endl;}student& operator=(const student& s){//1.不能自己给自己进行赋值:if (this != &s){//2.分开显示的调用:preson::operator=(s);_score = s._score;_grade = s._grade;}return *this;}~student(){preson::~preson();cout << "~student" << endl;}
protected:int _score;int _grade;
};}

在这里插入图片描述

7.继承和友元+继承和静态:

1.继承和友元:

1.友元关系不可以继承,子类的友元不能访问子类的私有和保护。
2.如果想要使用友元函数那么应该在自己的类域中去声明友元函数。

2.继承和静态:

1,父类定义了一个static成员那么无论有多少子类继承,产生了多少对象,都只有这一个static成员实例。

8.多继承:

1.单继承:一个子类只去直接继承一个父类。

class A {
public:A(int a):_a(a){cout << "A()" << endl;}
protected:int _a;
};class B : public A{
public:B(int a, int b):A(a),_b(b){cout << "B()" << endl;}
protected:int _b;
};class C : public B {
public:C(int a, int b,int c):B(a,b), _c(c){cout << "B()" << endl;}
protected:int _c;
};

2.多继承:一个子类直接继承多个父类。

class A {
public:A(int a):_a(a){cout << "A()" << endl;}
protected:int _a;
};class B{
public:B(int b):_b(b){cout << "B()" << endl;}
protected:int _b;
};class C : public B,public A {
public:C(int a, int b,int c):B(b),A(a), _c(c){cout << "B()" << endl;}
protected:int _c;
};

1.菱形继承:特殊的多继承

在这里插入图片描述

菱形继承存在两个问题:
1.数据冗余:存储了很多不需要的数据。
2.二意性:作为老师有一个名称,作为学生有一个名称。

class preson {
public:preson(string name):_name(name){}protected:string _name;
};class student : public preson{
public:student(string name , int id):preson(name),_student_id(id){}
protected:int _student_id;
};class Teacher : public preson {
public:Teacher(string name, int id):preson(name), _teacher_id(id){}
protected:int _teacher_id;
};class Assistan : public student,public Teacher {
public:Assistan(string name_1 , string name_2,int id_1 , int id_2 , int money):student(name_1,id_1),Teacher(name_2,id_2),_money(money){}
protected:int _money;
};

1.person有一个属性名称。
2.作为学生有学生编号和名称。
3.作为老师有老师编号和名称。
4.作为助理继承了老师和学生的两个身份同时可以有工资这个属性。
5.这么一看好像没有什么问题但是人的公有属性名称在内存中的存贮是什么样子的?
6.如果说presson类有非常多的数据名字,住址,性别,身份证号等等,我们需要存贮两份数据吗?

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

2.1.菱形虚拟继承:

class A {
public:A(int a = 0):_a(a){cout << "A()" << endl;}
protected:int _a;
};class B : virtual public A{
public:B(int a , int b):A(a),_b(b){cout << "B()" << endl;}
protected:int _b;
};class C : virtual public A{
public:C(int a, int c):A(a),_c(c){cout << "B()" << endl;}
protected:int _c;
};class D :  public B , public C{
public:D(int a,int b,int c,int d):B(a,b),C(a,c),_d(d){}
protected:int _d;
};

为什么有虚继承?
在语法上新增加了一个虚继承去解决数据冗余和二意性。
注意:虚拟继承可以解决菱形继承继数据冗余和二意性问题,只能在B和C中去使用虚拟继承就可以解决问题。从底层来看相同的数据只会去保存一份节省了空间。

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

在这里插入图片描述

9.继承的总结和反思:

1.多继承

1.多继承是C++的缺陷,有了多继承就有菱形继承,不得不新增语法解决问题。

2组合和继承:

1.我们应该优先去使用组合而不是继承。
黑箱:组合
1.我们使用组合对象之间关系不太一个类被维护不会影响到另一个类。
2.为什么称为黑箱呢?
3.使用一个对象我们只需要去使用对象的接口我们不需要关心对象中内容只需要去使用相关的接口。对象里面的内容我们是看不见的!
4.组合之间没有非常强的依赖关系,耦合度底。

白箱:继承
1.对于子类来说父类就是一个白箱,父类中的内容我们大部分都可以访问到。操>作父类内容比较麻烦可能产生问题,
2.改变父类会影响子类,子类和父类的依赖关系是非常强的。
3.代码的耦合度是非常高的。

3.继承的作用:

在实际情况中可以使用组合就使用组合,组合的耦合度底,代码方便维护,对于继承来说合适使用继承的地方还是可以去使用继承,多态的前提条件也是需要有继承关系的。

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

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

相关文章

第十二篇【传奇开心果系列】Python的OpenCV技术点案例示例:视频流处理

传奇开心果短博文系列 系列短博文目录Python的OpenCV技术点案例示例短博文系列短博文目录一、前言二、视频流处理介绍三、实时视频流处理示例代码四、视频流分析示例代码五、归纳总结系列短博文目录 Python的OpenCV技术点案例示例短博文系列 短博文目录 一、前言 OpenCV视频…

程序报错无法打开源文件stdafx.h

在运行代码时&#xff0c;代码中头文件突然报错程序无法打开源文件stdafx.h include “stdafx.h”,编译器就说无法打开源文件&#xff0c;直接上干货解决方法是&#xff1a; 1.打开项目 ->项目属性&#xff08;最后一个&#xff09;-> C/C ->常规&#xff0c; 2在附…

【工作周志】240129-240204

本周学习了AXI相关的内容 AMBA &#xff08;Advanced Microcontroller Bus Architecture&#xff09; AXI &#xff08;Advanced eXtensible Interface&#xff09; ARM公司提出&#xff0c;AMBA3.0协议中重要组成部分&#xff0c;是一种面向高性能、高带宽、低延迟的片内总线…

【c++】vector用法详解

vector用法详解 vector定义vector容器的构造函数vector容器内元素的访问1.通过下标 [ ]来访问2.通过迭代器来访问3.通过范围for来访问 vector常用函数的用法解析1.size()2.clear()3.capacity()4.reserve()5.resize()6.shrink_to_fit()7.pop_back()8.push_back()9.erase()10.in…

python基于django的公交线路查询系统mf383

1.个人信息的管理&#xff1a;对用户名&#xff0c;密码的增加、删除等 2.线路信息的管理&#xff1a;对线路的增加、修改、删除等 3.站点信息的管理&#xff1a;对站点的增加、修改、删除等 4.车次信息的管理&#xff1a;对车次的增加、修改、删除等 5.线路查询、站点查询 …

已解决: ModuleNotFoundError: No module named ‘tensorflow‘ 问题

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

nba2k24 球魁面补【23-24通用】

nba2k24 球魁面补 nba2k23-nba2k24通用 球魁面补 下载地址&#xff1a; https://www.changyouzuhao.cn/9841.html

蓝桥杯省赛无忧 课件91 高斯消元

01 算法概述 02 问题引入 03 算法分析 04 例题

Linux 多线程 | 线程的概念

线程的概念 线程是一个执行分支&#xff0c;执行粒度比进程更细&#xff0c;调度成本更低&#xff1b; 线程是进程内部的一个执行流&#xff1b; 线程是CPU调度的基本单位&#xff0c;进程是承担分配系统资源的基本实体。 之前我们学习过虚拟地址空间的知识&#xff0c;知道…

NetSuite 权限不足用户如何查询完整数据

假设我们做了一个Saved Search&#xff0c;用于统计所有涉及库存的事务类型&#xff0c;包括出入库、库存调整、生产报工、拆解、Standalone Invoice和Bill&#xff0c;等等。通过合计这些事务类型&#xff0c;我们就可以得到一个存货报表&#xff0c;能够得到任一时间点的库存…

线程同步解析

一 线程同步 1 同步的意义 现实中抢票可能没票了还在抢票&#xff0c;然后线程就会一直在加锁解锁&#xff0c;就会导致其它线程抢不到锁而产生饥饿问题&#xff0c;我们前面也提过usleep就是让线程被切换&#xff0c;能让其它线程去申请锁&#xff0c;这种方式并不好&#xf…

蓝桥杯备战——13.PCF8591芯片的使用

目录 1.芯片简介2.读写时序3.控制字4.代码封装库5.原理图分析6.使用示例 1.芯片简介 截取自NXP的PCF8591芯片数据手册&#xff0c;我把重点关注部分划出来了&#xff0c;请务必自行阅读一遍数据手册&#xff01; 2.读写时序 ①器件地址&#xff1a; Bit0决定是读还是写操作&…

最新GPT4.0使用教程,AI绘画,GPT语音对话使用,DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

优质成长:新生儿补充维生素B6的关键注意事项

引言&#xff1a; 维生素B6&#xff0c;作为B族维生素的一员&#xff0c;对于新生儿的神经系统发育和代谢功能至关重要。本文将深入探讨维生素B6的作用、新生儿补充的必要性&#xff0c;以及在补充维生素B6时应该注意的事项&#xff0c;为父母提供科学、全面的育儿指南。 第一…

WebChat——一个开源的聊天应用

Web Chat 是开源的聊天系统&#xff0c;支持一键免费部署私人Chat网页的应用程序。 目录树 TOC &#x1f44b;&#x1f3fb; 开始使用 & 交流&#x1f6f3; 开箱即用 A 使用 Docker 部署B 使用 Docker-compose 部署C 使用 Jar包 本地部署 ⌨️ 本地开发&#x1f91d; 参与…

开源浏览器Firefox:使用Docker本地部署并远程访问进行测试

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 部署Firefox二. 本地访问Firefox三. Linux安装Cpolar四. 配置Firefox公网地址…

网络原理-TCP/IP(5)

TCP协议 延迟应答 它也是基于滑动窗口,提高效率的一种机制,结合滑动窗口以及流量控制,能够以延迟应答ACK的方式,把反馈的窗口,搞大.核心在于允许范围内,让窗口尽可能大. 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小. 1.假设接收端缓冲区为1M.一次收到了5…

树状数组相关

前置细节 &#xff0c;得到转二进制后&#xff0c;从右往左数第一个1与之前所有的0构成数树状数组最终形成如上图结构维护的是的信息&#xff0c;如即到上层&#xff0c;包含当前区间的大区间&#xff0c;如即到同一层的前一个&#xff0c;与当前区间无关的另一同等大小的区间…

第十二讲_JavaScript浏览器对象模型BOM

JavaScript浏览器对象模型BOM 1. 浏览器对象模型介绍2. location2.1 常用的属性2.2 常用的方法 3. navigator3.1 常用的属性 4. history4.1 常用的方法&#xff1a; 5. 本地存储 1. 浏览器对象模型介绍 BOM(Browser Object Model) 是指浏览器对象模型&#xff0c;浏览器对象模…

Git--07--GitExtension

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、GitExtension下载GitExtension官网下载教程 二、GitExtension安装三、GitExtension配置四、GitExtension使用 一、GitExtension下载 官网下载&#xff1a; http…