继承杂谈。

在这里插入图片描述

内容一览

      • 前言
      • 继承的概念及定义
      • 继承的意义
      • 继承关系及访问限定符
      • 父类和子类对象之间的转化
      • 继承后的作用域
      • 继承与有元
      • 继承与静态成员
      • 多继承
      • 继承和组合的区别:
      • 继承的总结和反思

前言

 面向对象的三大特性:封装继承和多态,这三种特性优者很紧密地联系,但是也有很大的区别。
 对于刚接触面度对象编程风格的小白,你可能不了解继承到底是用来做什么的,以及为什么叫做继承,接下来我就带你仔细了解继承的作用以及注意事项。

继承的概念及定义

 继承是代码可以复用的重要手段,术语表达继承就是子类继承父类的属性和方法,大白话就是子承父业,子类可以拥有父类除了私有以外的成员和方法,还可以有自己的属性,父类通常也叫做基类,子类有时也叫做派生类。
 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,在之前我们在程序中的复用通常是函数复用,传入不同的参数,函数就可以产生不同的响应,但是在学完继承和多态后,代码的复用就不仅仅局限于函数的复用,继承就是类设计层次的复用。
举一个例子理解一下

class animal
{
public:
	void Paint()
	{
		cout << age << endl;
	}
protected:
	int age=10;
};

class cat :public animal
{
protected:
	int price = 500;
};

 动物是一个较大的概念,在动物的种类中有猫,小狗等等,他们具有都是动物的特点,也与属于他们自己的特点,继承animal后,cat就是animal的子类,animal就是cat的父类。
在这里插入图片描述
如上图所示,如果用子类构造出一个对象,子类也包含了父类的成员和方法。
此时,用父类函数构造出来的子类就也可以使用父类的方法。
在这里插入图片描述

继承的意义

 现在我们要思考了,为什么我们需要继承呢?向上变得例子来看,继承并没有带来太大的优化,这样你就大错特错了,如果仅仅只有两三个类,在每个类的属性和方法都确定的请款下确实某有必要实现继承,但是在现实中通常是一整个系统程序,往往有很多类且大部分类都是具有相似部分的,如果我们每个类都丹毒重新写,不仅代码臃肿而且工作量大,出bug不易查寻。
 有了继承之后,我们可以将部分类中相似的部分提取出来作为父类,在继承父类后添加自己新的属性和方法,这样不仅可以大大减少代码量,还易于维护,代码结构清晰可见。

继承关系及访问限定符

 C++中有三种访问限定符,分别是public,private,protected,继承方式也就有了三种,最常用的就是public继承。
在这里插入图片描述
如上图所示
私密性public<protected<private。
 刚开始时是没有保护这个限定符的,正式出现继承和多态之后才加上的,父类的私有子类不能访问,但父类的保护子类也可以访问。

父类和子类对象之间的转化

子类的对象可以赋值给父类的对象/父类的指针/父类的引用,这里有一种很形象的说法就是切片或者切割,子类的对象继承了父类,所以包含父类的一部分,在赋值给父类对象时,将子类父类的一部分切割给父类对象。
切记!只有子类对象可以赋值给父类。

	cat c;
	animal a = c;//子类对象赋值给父类
	animal* a = &c;//赋值子类对象的指针
	animal& k = c;//赋值子类对象的引用

赋值切片的过程类似于
在这里插入图片描述

继承后的作用域

 继承后子类和父类仍然是两个独立的作用域。所以要注意这个问题,如果子类和父类包含有同名函数,这两个函数之间的关系不是重载(重载要求两个函数在同一作用域),而是隐藏,子类会隐藏父类的同名函数。
 要注意哈!在父子类中,只要两函数的名称相同就构成隐藏。但是在实际中最好不要在父子类中定义同名函数,不然会容易混淆。
 如果构成隐藏后,我们调用该函数就调用的是子类的虚函数,如果我们想要调用父类的虚函数,就要加上作用域(在子类成员函数中,可以使用基类::基类成员,显式访问)。

class person
{
public:
	void func1()
	{
		cout << " person::func1" << endl;
	}
};
class student : public person
{
public:
	void func1()
	{
		cout << "func1" << endl;
	}
};

int main()
{
	person p;
	student s;
	p.func1();
	s.func1();
	return 0;
}

在这里插入图片描述
 运行后发现,父类调用该函数使用的父类中的,子类调用就调用子类中的函数,子类将父类的同名函数进行隐藏,这样搞调用时就不会产生歧义。
接下来探索探索继承后,子类的成员函数和弗雷德成员函数之间的关系。

  • 1,首先,子类的构造函数必须先调用父类的构造函数初始化子类中所包含父类的一部分,如果父类中没有默认的构造函数,就必须在子类构造函数的初始化列表进行初始化。
  • 2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  • 3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  • 4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  • 5. 派生类对象初始化先调用基类构造再调派生类构造。
  • 6. 派生类对象析构清理先调用派生类析构再调基类的析构。

通过调试来带领大家观察构造及西狗屎的顺序。
在这里插入图片描述
 可以发现,在构造子类对象时,再初始化列表首先调用父类的构造函数,在析构时,首先调用子类的析构函数,然后再调用父类的析构函数。
在这里插入图片描述

继承与有元

 我们知道一个类的有元可以在类外使用类中的变量和方法,那么父类的友元函数能被继承吗?
 答案是不能,友元关系不能被继承,基类的有缘不能访问子类的私有和保护。
举一个例子

class A
{
public:
private:
	int _a=2;
};
class B:public A
{
public:
	int _b = 0;
	friend void Printf();
};
void Printf(B b,A a)
{
	cout << b._b;
	cout << a.a;
}
int main()
{
	A a;
	B b;
	Printf(b,a);
	return 0;
}

 在子类中创建一个友元函数,友元函数只可以访问,而不是父类的友元函数,所以父类中的保护和私有我们这个友元函数不能访问。
在这里插入图片描述

继承与静态成员

 基类定义了static静态成员,整个继承体系里面只有一个这样的成员,无论有多少个子类,静态成员就只有这一个。

class A
{
public:
	int _a = 2;
	static int aa;
};
int A::aa=1;
class B :public A
{
public:
	int _b = 0;
};
int main()
{
	A a;
	B b;
	A::aa++;
	cout<<a.aa<<endl;
	a.aa++;
	cout << b.aa;
	return 0;
}

 由于两个类中的静态变量只有一份,所以我们可以直接用类加作用域直接访问,也可以用对象进行访问。
如图
在这里插入图片描述

多继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
例如:
在这里插入图片描述

多继承:一个子类同时继承多个父类,这种关系称为多继承
在这里插入图片描述
有了多继承就会产生一种特殊的情况,那就是菱形继承
在这里插入图片描述
这种情况就会产生特殊的影响
 假如A中有一个元素,那么BC分别继承了A,然后D再次继承BC,D中就包含了两份这个元素。如果我们去调用D中A的元素(假设为_a)就会出现二义性。
在这里插入图片描述
 不仅如此,我们D类中包含了两份_a,还会浪费内存。我们可以使用作用域去看。
在这里插入图片描述
如何解决数据重复问题呢?
我们可以使用关键字virtual。
使用虚拟继承就可以解决问题
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
关键字virtual在多态时也会用到,在这里是修饰继承了最上边的父类的子类。在上边的例子中就是修饰B和C。
在这里插入图片描述
正如上图,也是菱形继承的一种,我们要在距离被继承两次的父类的下一层子类加virtual,使其变成虚拟继承。
但是还是那句话,我们在实际写程序中尽量不要写菱形继承。
如下图
在这里插入图片描述
此时再来观察在内存中是如何解决这件问题的。
借用上边的例子
在这里插入图片描述
我们来调试运行看一看。
在这里插入图片描述
 可以发现,不管我们调用B还是C对象中的_a元素,都是调用的同一个,来看一看底层如何实现的,以及为什么将A类中的_a放在最后的位置。
我们在X86的环境下来观察
在这里插入图片描述
 可以发现,在B和C对象前都有一个指针,这个指针里存的就是该位置到_a的距离,当我们想要修改A中_a的值时,就会找到该位置距离A对象中_a的长度,然后修改A中_a的值。
 将A中数据放在末尾就是为了统一管理,方便每一个继承该父类的子类通过指针找到该位置。

继承和组合的区别:

public是一种is-a的关系,也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系,假设B组合了A,每个B对象中都会有一个A对象。
在这里插入图片描述
优先使用对象组合,而不是类继承。
 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。
 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。

继承的总结和反思

 继承大大增加了我们代码的复用性,有人说C++语法复杂,其实就是多继承的问题,比建议设计出多继承,java就吸取了C++的教训,没有引入多继承,如果使用多继承不小心设计出了菱形继承,那会在复杂度和时间上都产生问题。
本文结束,如果有问题或者有疑问就直接评论哦,回复很快。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于神经网络的偏微分方程求解器再度取得突破,北大字节的研究成果入选Nature子刊

目录 一.引言:神经网络与偏微分方程 二.如何基于神经网络求解偏微分方程 1.简要概述 2.基于神经网络求解偏微分方程的三大方向 2.1数据驱动 基于CNN 基于其他网络 2.2物理约束 PINN 基于 PINN 可测量标签数据 2.3物理驱动(纯物理约束) 全连接神经网路(FC-NN) CN…

C++特殊类设计【特殊类 || 单例对象 || 饿汉模式 || 懒汉模式】

目录 一&#xff0c;特殊类设计 1. 只在堆上创建的类 2. 只允许在栈上创建的类 3. 不能被继承的类 4. 不能被拷贝的类 5. 设计一个类&#xff0c;只能创建一个对象&#xff08;单例对象&#xff09; 饿汉模式 懒汉模式 C11静态成员初始化多线程安全问题 二&#xff…

Android14 Handle机制

Handle是进程内部, 线程之间的通信机制. handle主要接受子线程发送的数据, 并用此数据配合主线程更新UI handle可以分发Message对象和Runnable对象到主线程中, 每个handle实例, 都会绑定到创建他的线程中, 它有两个作用,: (1) 安排消息在某个主线程中某个地方执行 (2) 安排…

工作纪实46-关于微服务的上线发布姿势

蓝绿部署 在部署时&#xff0c;不需要将旧版本的服务停掉&#xff0c;而是将新版本与旧版本同时运行&#xff0c;新版本测试无误之后再将旧版本停掉。这样可以避免再升级的过程中如果失败服务不可用的问题&#xff0c;因为同时部署了两个版本的程序&#xff0c;使得硬件资源是…

算法设计.

文章目录 1. 贪心算法&#xff1a;只看当前1.1 零钱兑换问题&#xff1a;力扣322 2. 活动选择问题3. 动态规划3.1 不同路径&#xff1a;3.2 0-1背包问题3.3 完全背包问题3.4 零钱兑换-动态规划 4. 最长公共字串--动态规划5. 最长公共子序列6. 最长递增子序列7. 打家劫舍8. 全排…

分销商城小程序怎么做_打造高效分销商城小程序的秘诀

在数字化浪潮席卷全球的今天&#xff0c;小程序成为了连接线上线下的重要桥梁。其中&#xff0c;分销商城小程序因其独特的裂变传播能力和低门槛的创业模式&#xff0c;受到了越来越多创业者和商家的青睐。那么&#xff0c;如何打造一个高效、吸引人的分销商城小程序呢&#xf…

数据库(一)初步认识数据库系统

什么是数据库&#xff1f; 表&#xff1a;以按行按列形式组织及展现的数据 如下便是一个表&#xff0c;也叫关系&#xff0c;描述了一批相互有关联关系的数据 数据库&#xff1a;起源于规范化表&#xff08;如成绩单&#xff09;的处理&#xff0c;简称DB&#xff0c;是相互有…

基于鳑鲏鱼优化算法(Bitterling Fish Optimization,BFO)的无人机三维路径规划

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化飞行…

童装WP模板

童装WP模板 https://www.wpniu.com/moban/6359.html

JDBC和连接池

JDBC和连接池 大纲 JDBC连接数据库的方式 具体案例 JDBC 需求&#xff1a;满足Java程序能对多个不同的数据库进行操作&#xff0c;而创建了一种接口&#xff0c;实现对数据库的规范 连接数据库的方式 1.方法1 先创建一个Driver对象&#xff0c;然后设置连接到的数据…

微信小程序提示确认框

如图所示&#xff0c;如何弹出微信小程序自带默认弹框&#xff1f; 代码如下&#xff1a; wx.showModal({ title: 确认, content: 确定要删除吗&#xff1f;, success (res) { if (res.confirm) { console.log(用户点击确定) } else if (res.cancel) { console.log(用…

物联网电气融合实训室建设方案

1 教学实训总体设计 1.1 建设背景 &#xff08;一&#xff09;政策推动与战略部署 近年来&#xff0c;物联网技术在全球范围内得到了广泛的关注和应用。作为信息技术的重要组成部分&#xff0c;物联网在推动经济转型升级、提升社会管理水平、改善民生福祉等方面发挥着重要作…

二、TensorFlow结构分析(5)案例

案例&#xff1a; minimize(error) 代码&#xff1a; def linear_regression():# 自实现线性回归# 1&#xff09;准备数据X tf.random.normal(shape[100,1])y_true tf.matmul(X,[[0.8]]) 0.7# 2&#xff09;构造模型# 定义模型参数 用 变量weights tf.Variable(initial_v…

Linux的基本权限

一、对shell的浅显认识 shell是操作系统下的一个外壳程序&#xff0c;无论是Linux操作系统&#xff0c;还是Windows操作系统&#xff0c;用户都不会直接对操作系统本身直接进行操作&#xff0c;需要通过一个外壳程序去间接的进行各种操作 在Linux的shell外壳就是命令行&#…

Storyboard动画、EventTrigger事件触发器

就是动画&#xff0c;要注意的就是EventTrigger中的SourceName就是想要实现这个功能的按钮 <StackPanel Orientation"Vertical"><Rectanglex:Name"rect"Width"200"Height"40"Fill"Pink" /><StackPanel Orie…

java之lombok

Lombok是一个实用的java类库&#xff0c;能通过注解的形式自动生成构造器&#xff0c; getter setter equsls hashcode tostring等方法 并且可以自动化生成日志变量&#xff0c;简化java开发&#xff0c;提高效率 作用 导入 <dependency><groupId>org.projectlomb…

Servlet API 详细讲解

Servlet API 详细讲解 API就是一组类和方法的集合&#xff0c;servlet 中的 类是非常多的&#xff0c;咱们只需要学习 3个类即可。 HttpServletHttpServletRequest&#xff08;服务器如何读取客户端响应&#xff09;HttpServletResponse&#xff08;服务器如何把响应返回给客…

C++ 中的头文件和源文件

#include<>一般用于包含系统头文件&#xff0c;诸如stdlib.h、stdio.h、iostream等&#xff1b; 类库目录下查找失败&#xff0c;编译器会终止查找&#xff0c;直接报错&#xff1a;No such file or directory. #include""一般用于包含自定义头文件&#xff…

Ubuntu18.04 下使用 Pybind11实现 C++ 调用 Python 函数和类的示例

Pybind11 是一个轻量级的库&#xff0c;它提供了在 C 中无缝集成 Python 代码的能力。使用 Pybind11&#xff0c;你可以很容易地从 C 调用 Python 代码&#xff0c;反之亦然。下面我将通过一个简单的例子来展示如何在 Ubuntu 系统上使用 Pybind11 从 C 调用 Python 接口。 安装…

css--浮动

一. 浮动的简介 在最初&#xff0c;浮动是用来实现文字环绕图片效果的&#xff0c;现在浮动是主流的页面布局方式之一。 二. 元素浮动后的特点 &#x1f922;脱离文档流。&#x1f60a;不管浮动前是什么元素&#xff0c;浮动后&#xff1a;默认宽与高都是被内容撑开&#xff0…