继承和多态(上)

news2024/9/22 19:32:45

目录

继承

继承方式

切片(切割)

 重定义(隐藏)

继承的6个默认成员函数

继承与友元,静态成员

菱形继承

菱形继承的冗余和二义性

 继承和组合


继承

什么是继承?

是代码复用的一种手段。

语法:

父类也叫基类(Base)

子类也叫派生类(Derived) 

class Person
{
private:
	char sex;
};

class Student :public Person
{
private:
	string _name;
	string _tel;
};

继承方式

分为3大类,public继承,protected 继承,private继承

 不可见就是无论在类外还是在类内都不能使用该继承而来的成员变量。

切片(切割)

将子类赋值给父类是因为数据的类型不同,会发生类型转换,该类型转换就是切片(切割)

class Base
{
public:
	Base(int a = 1)
	{
		_a = a;
	}
	void print()
	{
		cout << _a << endl;
	}
	int _a;
};

class Derived :public Base
{
public:
	Derived(int b = 2)
	{
		_b = b;
	}
	int _b;
};

int main()
{
	Derived b(3);
	Base a;
	//子类可以赋值给父类的对象,指针和引用
	a = b;
	Base* c = &b;
	Base& d = b;
	//父类不能赋值给子类对像
	//b = a;
	//父类指针可以强转为子类指针但使用强转后的父类指针可能出现越界问题
	Derived* f = (Derived*)c;
	//越界
	f->_b;
	return 0;
}

如何实现子类到父类的赋值的?

 重定义(隐藏)

父类和子类中有同名的成员或同名的函数,调用子类中的同名成员是就会屏蔽父类中的同名成员。

注意:只要同名就构成屏蔽(重定义)

class Base
{
public:
	Base(int a = 1)
	{
		_a = a;
	}
	void print()
	{
		cout << _a << endl;
	}
	int _a;
};

class Derived :public Base
{
public:
	Derived(int b = 2)
	{
		_b = b;
	}
	void print()//重定义的成员函数
	{
		cout << _a << endl;
		cout << _b << endl;
	}
	double _a = 1.1;//重定义的成员变量
	int _b;
};

重定义的本质:基类和派生类的作用域不同,重定义的本质就是如果构成重定义,默认情况下就不会去基类中去查找同名的成员。

如何去访问基类的同名成员?

以上知识默认情况下,当然也可以指定类域去访问基类的同名,这样就可以访问基类的同名成员了。

	Base a;
	a.print();
	Derived b;
	b.print();
	b.Base::print();//指定类域去访问同名基类成员
class Base
{
public:
	Base(int a = 1)
	{
		_a = a;
	}
	void print()
	{
		cout << _a << endl;
	}
	int _a;
};

class Derived :public Base
{
public:
	Derived(int b = 2)
	{
		_b = b;
	}
	void print(int b)//重定义的成员函数
	{
		cout << _a << endl;
		cout << _b << endl;
	}
	double _a = 1.1;//重定义的成员变量
	int _b;
};

基类中pirnt成员函数是否与派生类中的print成员函数构成函数重载?

答案是否,因为函数重载的条件是,同一作用域,函数名相同,参数列表不同。

因为基类的print和派生列的print不在同一作用域,所以两个print不构成函数重载。

继承的6个默认成员函数

 1.派生类的初始化也需要初始化基类的成员,所以需要调用基类的构造函数,如果基类没有默认构造函数的话,那么就需要在初始化列表中显示调用基类的默认构造函数。

class Base
{
public:
	//此时基类没有默认构造函数
	Base(int a)
	{
		_a = a;
	}
	void print()
	{
		cout << _a << endl;
	}
	int _a;
};

class Derived :public Base
{
public:
	Derived(int b = 2)
		:Base(1)//基类没有默认构造函数,必须显示调用基类的构造函数
	{
		_b = b;
	}
	void print(int b)//重定义的成员函数
	{
		cout << _a << endl;
		cout << _b << endl;
	}
	double _a = 1.1;//重定义的成员变量
	int _b;
};

所以完成派生类的初始化时,需要调用基类的构造函数,基类和派生类构造函数的调用顺序是先调用基类的在调用派生类的。

为什么?

因为派生类的初始化可能用到基类的成员。

比如,以下这个例子,如果不先调用基类的构造函数,那么派生类的成员就无法正常完成初始化

class Base
{
public:
	//此时基类没有默认构造函数
	Base(int a)
	{
		_a = a;
	}
	void print()
	{
		cout << _a << endl;
	}
	int _a;
};

class Derived :public Base
{
public:
	Derived(int b = 2)
		:Base(1)//显示调用基类的构造函数
		,_b(_a)//用基类的成员初始化
	{
		;
	}
	void print(int b)//重定义的成员函数
	{
		cout << _a << endl;
		cout << _b << endl;
	}
	int _b;
};

2.析构函数是先派生类自己的析构函数,然后再调用自动调用基类的析构函数(这里是基类中的析构函数,不单单是指默认析构函数)。

这里的自动调用是编译器的工作,意思就是我们不用显示调用,编译器会完成。 

为什么是这样的顺序呢?

因为在派生类析构的时候可能调用基类的成员,如果提前析构了,那么就无法找到该变量。

class Derived :public Base
{
public:
	Derived(int b = 2)
		:Base(1)//显示调用基类的构造函数
		,_b(_a)//用基类的成员初始化
	{
		;
	}
	void print(int b)//重定义的成员函数
	{
		cout << _a << endl;
		cout << _b << endl;
	}
	~Derived()
	{
		Base::~Base();//先调用基类的析构
		cout << _a << endl;//但后续需要用到基类的成员
		cout << "~Derived()" << endl;

	}
	int _b;
};

 由以上的运行结果可知无论是否显示调用基类的析构函数,编译器都会自动调用基类的析构函数一次,有以前的知识可知,有的类不能调用两次析构函数,所以不要显示调用基类的析构函数。

析构函数因为重写(后续会谈),而重写需要函数同名,很明显表面上基类和派生类的析构不同名,而编译器会将两个析构函数都处理为destructor()函数,这样就同名了。如果不加virtual修饰,那么基类和派生类的析构函数就构成重定义(隐藏),基类的析构加上virtual就构成重写。

3.完成派生类的拷贝构造,赋值重载都需要调用基类中相应的函数,完成基类中拷贝构造或赋值。

继承与友元,静态成员

友元是不能继承的。

在整个继承体系中只有一个静态成员。

菱形继承

菱形继承的冗余和二义性

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;
};

 

由图可知D中有两个_a有冗余,并且如果不指定类域的话不知道_a是继承的B的_a,还是继承的C的_a。

这就是菱形继承的冗余和二义性。

菱形D类在内存中的结构

 

如何解决菱形继承中的冗余和二义性?

使用虚继承,

class A
{
public:
	int _a;
};
class B :virtual public A//虚继承A
{
public:
	int _b;
};
class C :virtual public A//虚继承A
{
public:
	int _c;
};
class D :public B, public C
{
public:
	int _d;
};

虚继承的解决原理就是 在B类和C类中有个指针指向续基表,虚基表中存的是偏移量,偏移量就是相应类的_a在D类的偏移量。

 

class Person
{
public :
 string _name ; // 姓名
};
class Student : virtual public Person
{
protected :
 int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
 int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
 string _majorCourse ; // 主修课程
};
void Test ()
{
 Assistant a ;
 a._name = "peter";
}

 

 继承和组合

//继承
class A
{
public:
	int _a;
};
class B :public A
{
public:
	int _b;
};
//组合
class A
{
public:
	int _a;
};
class B 
{
public:
	A a;
	int _b;
};

public继承就是一种 is-a的关系,就比如人与黄种人的关系,黄种人是一种人。

组合就是一种 has-a 的关系,就比如汽车与轮胎的关系,轮胎是汽车的组成之一。

优先使用组合,后考虑继承。

1.继承是一种白箱复用,基类的细节派生类可见,封装性低;基类和派生类依赖性强,耦合度高。

2.组合是一种黑箱复用,依赖性弱,耦合度低。

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

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

相关文章

生物科技食品公司企业网站模板带手机端:完整源代码包及搭建教程

系统概述 本模板设计秉承“科技引领健康&#xff0c;绿色塑造未来”的理念&#xff0c;融合生物科技的先进性与食品行业的健康属性&#xff0c;通过简洁大气的界面布局、清新自然的色彩搭配以及流畅的用户体验&#xff0c;展现企业的专业实力与品牌形象。无论是产品展示、企业…

Java 实验四:类和对象的应用

一、实验目的 1、掌握类的声明、对象的创建、方法的定义和调用、构造函数的使用。 二、实验环境 1、windows11; 2、JDK1.8,集成开发环境Eclipse。 三、实验内容 &#xff08;一&#xff09;定义一个表示学生信息的类Student ①类Student的成员变量&#xff1a; sNo …

Java 客户端操作 Redis 命令(端口号映射方法,命令演示,注意事项)

文章目录 开放端口号问题引入依赖验证连接通用命令使用set 和 get 命令的使用exists 和 del 命令的使用keys 命令的使用expire 和 ttl 命令type 命令的使用 String 类型命令使用mset 和 mget 命令getrange 和 setrange 命令append 命令incr 和 decr 命令 list 类型命令使用lpus…

AI会不会让更多人失业?

最近网上热议的内容&#xff1a;武汉市萝卜快跑无人驾驶的网约车与出租车抢生意&#xff0c;惹来了很多人的非议。 有的人说AI科技应该是帮助人们去做一些高危险的事情&#xff0c;或者是一些比较脏&#xff0c;累的工作&#xff0c;比如救灾&#xff0c;排爆&#xff0c;航天探…

docker emqx 配置密码和禁用匿名连接

mqtt版本emqx/emqx:4.4.3 1.首先把镜像内目录/opt/emqx/etc拷贝到本地 2.做映射 3.allow_anonymous&#xff0c; false改成true 4. 5.MQTTX连不上的话看看下图的有没有打开

电商数据分析在品牌控价中的关键作用

品牌在进行控价操作时&#xff0c;会运用电商价格监测系统来监测线上数据&#xff0c;这些数据可能涵盖本品牌以及竞品的数据&#xff0c;其数据量无疑是巨大的。倘若只是单纯地进行品牌数据监测&#xff0c;并输出所监测到的低价数据&#xff0c;对于数据的价值挖掘而言&#…

【线程安全】关于死锁问题

文章目录 死锁的基本概念死锁的四个必要条件避免死锁避免死锁的算法死锁检测算法 死锁的基本概念 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。当然&#xff0c;线程之间同样也有死…

Chrome浏览器的Profile数据内容简介

前文简介了Chrome存储的账密/Cookie数据&#xff1a;一段代码读取Chrome存储的所有账号密码和Cookie 本文再扩展介绍一下Chrome存储的其它一些隐私数据。 注&#xff1a;因为业务需要&#xff0c;简单调研了一些基本内容和存储路径&#xff0c;没有深入去研究&#xff0c;有啥…

C 语言指针进阶

1.0 指针的定义 指针是内存中一个最小单元的编号&#xff08;内存单元的编号称之为地址【地址就是指针指针就是地址】&#xff09;指针通常是用来存放内存地址的一个变量。本质上指针就是地址&#xff1a;口语上说的指针起始是指针变量&#xff0c;指针变量就是一个变量&#…

本地部署,Flash Diffusion: 加速条件扩散模型实现快速图像生成

目录 引言 技术背景 Flash Diffusion 的架构与原理 Flash Diffusion 的主要特点 本地部署 运行结果 实验结果与分析 应用实例 结论 GitHub - gojasper/flash-diffusion: Official implementation of ⚡ Flash Diffusion ⚡: Accelerating Any Conditional Diffusion M…

uniapp:国家、省市区,4级联动

使用uview的Select 列选择器 选择器完成国家&#xff0c;省市区&#xff0c;4级联动 要求后台数据格式&#xff1a; list: [{label: 中国,value: 1,children: [{label: 河南省,value: 2,children: [{label: 郑州市,value: 3,children: [{label: 中原区,value: 4},{label: 郑东…

系统架构师考点--系统安全

大家好。今天我来总结一下系统安全相关的考点&#xff0c;这类考点每年都会考到&#xff0c;一般是在上午场客观题&#xff0c;占2-4分。 一、信息安全基础知识 信息安全包括5个基本要素&#xff1a;机密性、完整性、可用性、可控性与可审查性 (1)机密性&#xff1a;确保信息…

嵌入式智能手表项目实现分享

简介 这是一个基于STM32F411CUE6和FreeRTOS和LVGL的低成本的超多功能的STM32智能手表~ 推荐 如果觉得这个手表的硬件难做,又想学习相关的东西,可以试下这个新出的开发板,功能和例程demo更多!FriPi炸鸡派STM32F411开发板: 【STM32开发板】 FryPi炸鸡派 - 嘉立创EDA开源硬件平…

IMS架构中的注册与会话流程:RTPEngine集成及消息路由详解

目录 S-CSCF 调用 RTPengine 整体路由 注意 IMS 注册流程 和 IMS 会话流程 的区别 IMS注册流程 IMS会话流程(如INVITE请求) 这种设计的原因 P-CSCF 调用 RTPengine S-CSCF 调用 RTPengine 整体路由 UA a生成SDP offer&#xff0c;发送SIP INVITE请求(包含SDP offer)&…

MySql 数据库 - 下载安装

MySQL数据库 简单介绍 数据库 数据存储的仓库数据库管理系统 操作和管理数据库的大型软件SQL 操作关系型数据库的变成语言&#xff0c;是一套标准 版本 MySQL官方提供了两种不同的版本&#xff1a; 社区版 免费&#xff0c;MySQL不提供任何的技术支持商业版 收费&#xff0c…

数据结构(单链表(1))

前言 线性表中有着许多的结构&#xff0c;如顺序表和链表。而单链表则是链表的最基础的一种形式&#xff0c;下面就让我们对其做一个了解。 概念 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次…

【python】Pandas中`ValueError: cannot reindex from a duplicate axis`错误分析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

构建GitLab代码私有仓库

构建代码私有仓库 公司代码仓库一般都放在git上&#xff0c;但为了安全一般都不会放在开放的git上&#xff0c;都会搭建自己的仓库&#xff0c;今天就记录一下git搭建的过程。以下安装过程以centos7为例&#xff1a; 步骤一&#xff1a;安装并配置依赖项&#xff0c;同时打开ht…

解决gitlab报502的问题

external_url http://10.7.24.6:10002 puma[port] 8091 sudo gitlab-ctl reconfigure sudo gitlab-ctl restart 设置管理员密码&#xff1a; 1. 切换目录&#xff1a;cd 安装目录gitlab的bin目录下 2. 以root执行 &#xff1a;gitlab-rails console命令&#xff0c;等待…

HTTP背后的故事:理解现代网络如何工作的关键(一)

一.HTTP是什么 概念 &#xff1a; 1.HTTP ( 全称为 " 超文本传输协议 ") 是一种应用非常广泛的 应用层协议。 2.HTTP 诞生与1991年. 目前已经发展为最主流使用的一种应用层协议. 3.HTTP 往往是基于传输层的 TCP 协议实现的 . (HTTP1.0, HTTP1.1, HTTP2.0 均为 T…