C++:类的继承和派生

2.1继承

继承是面向对象的主要特征(此外还要封装和多态)之一,它使得一个类从现有类中派生,而不必重新定义一个新类。继承的实质就是用已有的数据类型创建新的数据类型,并保存已有数据类型的特点,以旧类为基础创建新类,新类包含了旧类的数据成员和成员函数,并且可以在新类中添加新的数据成员和成员函数。旧类被称为基类或者父类,新类被称为派生类或子类。

2.1.1继承的基本语法

例如我们看到很多网页中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同。

#include<iostream>
using namespace std;
普通实现
//class Java
//{
//public:
//  void head()
//  {
//      cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//  }
//  void foot()
//  {
//      cout << "帮助中心、交流合作..." << endl;
//  }
//  void left()
//  {
//      cout << "Java、Python、C++..." << endl;
//  }
//  void content()
//  {
//      cout << "java学科视频" << endl;
//  }
//};
//class Python
//{
//public:
//  void head()
//  {
//      cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//  }
//  void foot()
//  {//      cout << "帮助中心、交流合作..." << endl;//  }//  void left()//  {//      cout << "Java、Python、C++..." << endl;//  }//  void content()//  {//      cout << "python学科视频" << endl;//  }//};...//继承实现页面class BasePage{public:void head(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void foot(){cout << "帮助中心、交流合作..." << endl;}void left(){cout << "Java、Python、C++..." << endl;}};class Java :public BasePage{public:void content(){cout << "java学科视频" << endl;}};class Python :public BasePage{public:void content(){cout << "python学科视频" << endl;}};class Cpp : public BasePage{public:void content(){cout << "C++学科视频" << endl;}};//继承的好处:减少重复代码//语法:class 子类:继承方式 父类//子类 也称为 派生类//父类 也称为 基类void test01(){cout << "Java页面如下" << endl;Java ja;ja.head();ja.foot();ja.left();ja.content();}int main(){test01();system("pause");return 0;}

总结:

继承的好处:可以减少重复的代码

class A:public B;

A类称为子类或派生类

B类称为父类或基类

派生类中的成员,包含两大部分:

一类是从基类继承过来的,一类是自己增加的成员。

从基类继承过来的表现起共性,而新增的成员体现了其个性。

2.1.2 继承的方式

继承方式有三种:

  1. 公共继承
  2. 保护继承
  3. 私有继承

#include<iostream>using namespace std;//继承方式//公共继承class Base1{public:int m_A;protected:int m_B;private:int m_C;};class Son1 :public Base1{public:void func() {m_A = 10;//父类中的公共权限成员,到子类中依然是公共权限m_B = 10;//父类中的保护权限成员,到子类中依然是保护权限//m_C=10;//父类中的私有权限成员 子类访问不到}};//保护继承class Base2{public:int m_A;protected:int m_B;private:int m_C;};class Son2 :protected Base2{void func() {m_A = 100;//父类中公共成员,到子类中变为了保护成员m_B = 100;父类中公共成员,到子类中变为了保护成员//m_C = 100;//父类中的私有成员 子类访问不到}};//私有继承class Base3{public:int m_A;protected:int m_B;private:int m_C;};class Son3 :private Base3{void func() {m_A = 100;//父类中公共成员,到子类中变为了私有成员m_B = 100;父类中公共成员,到子类中变为了私有成员//m_C = 100;//父类中的私有成员 子类访问不到}};class GrandSon3 :public Son3{public:void func(){m_A = 100;//报错,此时Son的时候已经为私有属性}};void test01(){Son1 son1;son1.m_A = 100;//son1.m_B = 100;//到Son1中m_B是保护权限}void test02(){Son2 s2;//s2.m_A = 100;//在son2中m_A变成了保护权限,因此类外访问不到}void test03(){Son3 s3;s3.m_A = 100;//到Son3中变为私有成员,类外访问不到s3.m_B = 1000;//到Son3中变为私有成员,类外访问不到}int main(){test01();system("pauese");}

2.1.3继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象?

#include<iostream>using namespace std;class Base{public:int m_A;protected:int m_B;private :int m_C;};class Son :public Base{public:int m_D;};void test01(){cout << sizeof(Son) << endl;//16 父类中所有非静态成员属性都会被子类继承下去//父类中私有成员属性是被编译器给隐藏了,因此是访问不到,但是确实是被继承下去了}int main(){test01();system("pause");return 0;}

2.1.4 继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数

就是按照栈的原理先进先出,首先子类先创建,所以会调用子类的构造函数,后面是父类的构造函数,销毁时是父类先调用析构函数,子类再调用析构函数。

2.1.5 继承同名成员处理

问题:当子类中出现与父类同名的成员,如何通过子类对象,访问到父类或子类中同名的数据呢?

  1. 访问子类同名成员 直接访问即可
  2. 访问父类同名成员 需要加作用域

#include<iostream>using namespace std;//继承中同名成员处理class Base{public:int m_A;Base(){m_A = 100;}void func(){cout << "Base-func()调用" << endl;}void func(int a){cout << "Base-func(int)调用" << endl;}};class Son :public Base{public:int m_A;Son(){m_A = 200;}void func(){cout << "Son-func()调用" << endl;}};//同名成员变量处理void test01(){Son s1;cout << s1.m_A<< endl;//子类中的100//如果通过子类对象访问父类中同名成员,需要加上作用域cout << s1.Base::m_A << endl;//父类中的200}//同名成员函数处理void test02(){Son s;s.func();//调用子类中的成员函数(直接调用子类中的成员函数)s.Base::func();//调用父类中的成员函数(加作用域调用父类中的成员函数)s.func(100);//错误//如果子类中出现和父类同名的成员函数,子类的同名成员函数会隐藏掉父类中所有同名成员函数//如果想要访问发哦父类中的同名成员函数,需要加作用域s.Base::func(100);}int main(){test02();test01();system("pause");return 0;}

总结:

  1. 子类对象可以直接访问到子类中同名成员。
  2. 子类对象加作用域可以访问到父类同名成员。
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数。

2.1.6 继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致。

#include<iostream>using namespace std;//class Base{public:static int m_A;static void func(){cout << "访问Base-func()函数" << endl;}};int Base::m_A = 100;class Son :public Base{public:static int m_A;static void func(){cout << "访问Son-func()函数" << endl;}};int Son::m_A = 200;//同名静态成员属性void test01(){Son s;//1、通过对象访问cout << s.m_A << endl;//访问子类中的静态成员变量cout << s.Base::m_A << endl;//访问父类中的静态成员变量//2、通过类名访问cout << Son::m_A << endl;//访问子类中的静态成员变量cout << Base::m_A << endl;//访问父类中的静态成员变量}//同名静态成员函数void test02(){Son s;//通过对象访问s.func();//访问子类中的成员函数s.Base::func();//访问父类中的成员函数//通过类名访问Son::func();//访问子类中的成员函数Base::func();//访问父类中的成员函数}int main(){test01();system("pause");return 0;}

总结:同名静态成员处理方式与非静态处理方式一样,只不过有两种访问的方式(通过类名和通过对象)。

2.1.7多继承语法

C++允许一个类继承多个类

语法:class子类:继承方式 父类1,继承方式2 父类…

多继承可能会引发父类中有同名成员出现,需要加作用域区分。

C++实际开发中不建议用多继承

#include<iostream>using namespace std;//父类class Base1{public:int m_A;Base1(){m_A = 100;}};class Base2{public:Base2(){m_A = 200;}int m_A;};//子类 需要继承Base1和Base2class Son1:public Base1,public Base2{public:Son1(){m_C = 300;m_D = 400;}int m_C;int m_D;};void test01(){Son1 s;cout << sizeof(s) << endl;//当父类中出现同名成员,需要加作用域区分cout << s.Base1::m_A << endl;cout << s.Base2::m_A << endl;}int main(){test01();system("pause");return 0;}

总结:多继承中如果父类出现了同名情况,子类使用时需要加作用域。

2.1.8菱形继承

菱形继承概念:
(1)两个派生类继承同一个基类;

(2)又有某个类同时继承两个派生类;

(3)这种继承被称为菱形继承,或者钻石继承。

#include<iostream>using namespace std;class Animal{public:int m_Age;};//利用虚继承,解决菱形继承的问题//Animal类称为虚基类class Sheep:virtual public Animal{public:};class Tuo:virtual public Animal{public:};class SheepTuo:public Sheep,public Tuo{public:};void test01(){SheepTuo st;st.m_Age = 18;//此时不会报错cout << st.Sheep::m_Age << endl;cout << st.Tuo::m_Age << endl;//菱形继承导致数据有两份,资源浪费}int main(){test01();system("pause");return 0;}

总结:

  1. 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  2. 利用虚继承可以解决菱形继承问题。

2.2 多态

2.2.1多态的基本概念

多态是C++面向对象三大特性之一

多态分为两类:

  1. 静态多态:函数重载和运算符重载属于静态多态,复用函数名
  2. 动态多态:派生类和虚函数实现运行时多态。

静态多态和动态多态区别:

  1. 静态多态的函数地址早绑定-编译阶段确定函数地址
  2. 动态多态的函数地址晚绑定-运行阶段确定函数地址

#include<iostream>using namespace std;//多态//动物类class Animal{public:virtual void speak()//加virtual关键字就会进行地址晚绑定{cout << "动物在说话" << endl;}};//猫类class Cat:public Animal{public:void speak(){cout << "小猫在说话" << endl;}};//狗类class Dog :public Animal{public://重写 函数返回值类型 函数名 参数列表 完全相同void speak()//在此前也可加virtual,可写可不写{cout << "小狗在说话" << endl;}};//执行说话的函数//地址早绑定 在编译阶段确定函数地址//如果想执行让猫说话,那么这个函数地址就不能早绑定,需要在运行阶段进行绑定,地址晚绑定//动态多态满足条件//1、有继承关系//2、子类要重写父类中的虚函数(如在此中的speak函数,父类中加了virtual关键字的函数)//动态多态的使用//父类的指针或者引用 执行子类对象(Animal &animal=cat)void doSpeak(Animal &animal)//Animal &animal=cat;//允许父类和子类的类型转换{animal.speak();}void test01(){Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);}int main(){test01();system("pause");return 0;}

总结:

父类满足条件:

  1. 有继承关系
  2. 子类重写父类中的虚函数

多态使用条件

  1. 父类指针或引用指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

 2.2.2纯虚函数和抽象类

在多态中,通常父类中虚函数的实现时毫无意义的,主要都是调用子类重写的内容。

因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名 (参数列表)=0;

当类中有了纯虚函数,这类也称为抽象类

抽象类特点:
(1)无法实例化对象;

  1. 子类必须重写抽象类中的纯虚函数,否则约束与抽象类。

#include<iostream>using namespace std;class Animal{public://纯虚函数//只要有一个抽象函数,这个类就称为抽象类//抽象类特点://1、无法实例化对象//2、抽象类的子类,必须重写父类中的纯虚函数,否则也属于抽象类virtual void func() = 0;};class Cat :public Animal{public:void func(){cout << "func函数调用" << endl;}};void test01(){//Animal a1;//报错Cat cat;Animal* animal = new Cat;animal->func();}int main(){test01();system("pause");return 0;}

2.2.3虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构代码改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

  1. 可以解决父类指针释放子类对象
  2. 都需要具体的函数实现

虚析构和纯虚析构区别:

  1. 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名()=0;

类名…~类名(){}

#include<iostream>using namespace std;//虚析构和纯虚析构class Animal{public:Animal(){cout << "Animal构造函数调用" << endl;}//虚析构和纯虚析构只能存在一个//利用虚析构可以解决父类指针释放子类对象时不干净的问题/*virtual ~Animal(){cout << "Animal虚析构函数调用" << endl;}*///纯虚析构 需要声明也需要实现//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象virtual ~Animal() = 0;virtual void speak() = 0;//纯虚函数(不需要实现)};Animal::~Animal(){cout << "纯虚析构函数调用" << endl;}class Cat:public Animal{public:Cat(string name){m_Name = new string(name);*m_Name=name;}virtual void speak(){cout << *m_Name<<"小猫在说话" << endl;}~Cat(){if (m_Name != NULL){cout << "cat析构函数" << endl;delete m_Name;m_Name = NULL;}}string* m_Name;};void test01(){Animal* animal = new Cat("Tom");animal->speak();//父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄露delete animal;}int main(){test01();system("pause");return 0;}

总结:

  1. 虚析构或纯虚析构就是用来解决提供父类指针释放子类对象
  2. 如果子类中没有堆区数据,可以不写虚析构或者纯虚析构
  3. 拥有纯虚析构的类也属于抽象类

2.3 友元

在程序中,有些私有属性也想让类外特殊的一些函数或者类访问,就需要用到友元的技术。

友元的目的就是让一个函数或者类访问另一个类中私有成员

友元的关键字friend

友元的三个实现:

  1. 全局函数做友元
  2. 类做友元
  3. 成员函数做友元

2.3.1全局函数做友元

#include<iostream>using namespace std;#include<string>//建筑物类class Building{friend void GoodGay(Building* building);//友元public:Building(){m_SettingRoom = "客厅";m_BedRoom = "卧室";}string m_SettingRoom;//客厅private:string m_BedRoom;//卧室};//全局函数做友元void GoodGay(Building* building){cout << "好基友的全局函数正在访问:" << building->m_SettingRoom << endl;cout << "好基友的全局函数正在访问:" << building->m_BedRoom << endl;}void test01(){Building building;GoodGay(&building);}int main(){test01();system("pause");return 0;}

2.3.2 类做友元

#include<iostream>using namespace std;#include<string>//建筑物类class Building{friend class GoodGay;//友元public:Building(){m_SettingRoom = "客厅";m_BedRoom = "卧室";}string m_SettingRoom;//客厅private:string m_BedRoom;//卧室};class GoodGay{public:GoodGay(){building = new Building;}void visit()//参观函数 访问Building中的属性{cout << "好基友类正在访问:" << building->m_SettingRoom << endl;cout << "好基友类正在访问:" << building->m_BedRoom << endl;}Building* building;};void test01(){GoodGay gg;gg.visit();}int main(){test01();system("pause");return 0;}

2.3.3 成员函数做友元

#include<iostream>using namespace std;#include<string>//建筑物类class Building{friend void GoodGay::visit();//友元public:Building(){m_SettingRoom = "客厅";m_BedRoom = "卧室";}string m_SettingRoom;//客厅private:string m_BedRoom;//卧室};class GoodGay{public:GoodGay();void visit();//参观函数 访问Building中的属性Building* building;};GoodGay::GoodGay(){building = new Building;}void GoodGay::visit(){cout << "好基友类正在访问:" << building->m_SettingRoom << endl;cout << "好基友类正在访问:" << building->m_BedRoom << endl;}void test01(){GoodGay gg;gg.visit();}int main(){test01();system("pause");return 0;}

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

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

相关文章

Python爬虫之Ajax数据爬取基本原理

前言 有时候我们在用 requests 抓取页面的时候&#xff0c;得到的结果可能和在浏览器中看到的不一样&#xff1a;在浏览器中可以看到正常显示的页面数据&#xff0c;但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档&#xff0c;而浏览器中…

编译安装mysql指定版本 shell脚本

要使用Shell脚本编译安装指定版本的MySQL&#xff0c;你需要下载源代码并按照官方文档的指引进行编译和安装。以下是一个简化的示例&#xff0c;用于下载、解压、编译和安装指定版本的MySQL&#xff1a; #!/bin/bash yum install -y wget cmake gcc yum install -y bzip2 bzi…

春节过半,预定的计划还没有开始

春节前就立下雄心勃勃的计划&#xff0c;想利春节假期开始搭一个人脸通WEB管理软件。但眼看春节过半&#xff0c;自己还没有开始动手呦。哎&#xff0c;突然紧张起来了。初二初三身体都不太舒服&#xff0c;不知道是怎么回事就感冒了&#xff0c;今晚更是高烧39.5&#xff0c;感…

【后端高频面试题--Linux篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;后端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 后端高频面试题--Linux篇 Windows和Linux的区别&#xff1f;Unix和Linux有什么区别&#xff1f…

2024春晚纸牌魔术原理----环形链表的约瑟夫问题

一.题目及剖析 https://www.nowcoder.com/practice/41c399fdb6004b31a6cbb047c641ed8a?tabnote 这道题涉及到数学原理,有一般公式,但我们先不用公式,看看如何用链表模拟出这一过程 二.思路引入 思路很简单,就试创建一个单向循环链表,然后模拟报数,删去对应的节点 三.代码引…

备战蓝桥杯---动态规划(入门1)

先补充一下背包问题&#xff1a; 于是&#xff0c;我们把每一组当成一个物品&#xff0c;f[k][v]表示前k组花费v的最大值。 转移方程还是max(f[k-1][v],f[k-1][v-c[i]]w[i]) 伪代码&#xff08;注意循环顺序&#xff09;&#xff1a; for 所有组&#xff1a; for vmax.....0…

python使用 sqlalchemy连接数据库帮助类

import mysql.connectorclass MySqlHelper(object):"""操作数据库帮助类"""def __init__(self):#self.host "localhost"#self.user "root"#self.password "xinshiyun123"#self.database "deliverunion_c…

GPT4:你是故意的吧!

请问下面选项中哪个是中文&#xff1f; A.Chinese B.英文 这是一个关于语言识别的问题。我们需要分析并确定所给选项中哪个表示中文。 对于选项A.Chinese&#xff1a;这个词本身表示“中文”或“中国的”。在多种语境中&#xff0c;它经常被用来指代中国的语言&#xff0c;即中…

VTK 三维场景的基本要素(相机) vtkCamera 相机的运动

相机的运动 当物体在处于静止位置时&#xff0c;相机可以在物体周围移动&#xff0c;摄取不同角度的图像 移动 移动分为相机的移动&#xff0c;和相机焦点的移动&#xff1b;移动改变了相机相对焦点的位置&#xff0c;离焦点更近或者更远&#xff1b;这样就会改变被渲染的物体…

C#,数值计算,矩阵的行列式(Determinant)、伴随矩阵(Adjoint)与逆矩阵(Inverse)的算法与源代码

本文发布矩阵&#xff08;Matrix&#xff09;的一些初级算法。 一、矩阵的行列式&#xff08;Determinant&#xff09; 矩阵行列式是指矩阵的全部元素构成的行列式&#xff0c;设A(a)是数域P上的一个n阶矩阵&#xff0c;则所有A(a)中的元素组成的行列式称为矩阵A的行列式&…

【开源】SpringBoot框架开发数字化社区网格管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、开发背景四、系统展示五、核心源码5.1 查询企事业单位5.2 查询流动人口5.3 查询精准扶贫5.4 查询案件5.5 查询人口 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数字化社区网格管理系统&#xf…

蓝桥杯——第 5 场 小白入门赛(c++详解!!!)

文章目录 1 十二生肖基本思路&#xff1a; 2 欢迎参加福建省大学生程序设计竞赛基本思路&#xff1a;代码&#xff1a; 3 匹配二元组的数量基本思路&#xff1a;代码: 4 元素交换基本思路&#xff1a;代码&#xff1a; 5 下棋的贝贝基本思路&#xff1a;代码&#xff1a; 6 方程…

【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、Cons…

【蓝桥杯】灭鼠先锋

一.题目描述 二.解题思路 博弈论&#xff1a; 只能转移到必胜态的&#xff0c;均为必败态。 可以转移到必败态的&#xff0c;均为必胜肽。 最优的策略是&#xff0c;下一步一定是必败态。 #include<iostream> #include<map> using namespace std;map<string,bo…

ChatGPT高效提问—prompt实践(生成VBA)

ChatGPT高效提问—prompt实践&#xff08;生成VBA&#xff09; 2. 生成VBA函数操作Excel ​ 当前Excel表格数据无背景颜色&#xff0c;区分不明显。假如我们想美化数据展示效果&#xff0c;把标题行设置为浅蓝色&#xff0c;其余奇数行设置为橙色&#xff0c;该怎么操作呢&am…

【项目日记(九)】项目整体测试,优化以及缺陷分析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

Tied Block Convolution: 具有共享较薄滤波器的更简洁、更出色的CNN

摘要 https://arxiv.org/pdf/2009.12021.pdf 卷积是卷积神经网络&#xff08;CNN&#xff09;的主要构建块。我们观察到&#xff0c;随着通道数的增加&#xff0c;优化后的CNN通常具有高度相关的滤波器&#xff0c;这降低了特征表示的表达力。我们提出了Tied Block Convolutio…

###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 一. 两个主要软件的介绍 1.KeiluVision5软件 Keil uVision5是一款集成开发…

分享87个jQuery特效,总有一款适合您

分享87个jQuery特效&#xff0c;总有一款适合您 87个jQuery特效下载链接&#xff1a;https://pan.baidu.com/s/1H9kH2qrL-AHFn3jDlNvTFw?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理…

MySQL(基础)

第01章_数据库概述 1. 为什么要使用数据库 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#xff0c;特别是企业级应用&#xff0c;数据持久化意味着将内存中的数据保存到硬盘上加以”固化”&#xff0c;而持久化的实现过程大多…