C++ —— 继承

news2024/11/19 13:45:17

什么是继承?

继承是指一种代码可以被复用的机制,在一个类的基础上进行扩展,产生的新类叫做派生类,被继承的类叫基类。(也可称为子类和父类)

继承的写法:

class B : 继承方式 A              (类B以public/private/protected的方式继承类A)

当需要继承多个类时:

 class C : 继承方式 A B              (类C以public/private/protected的方式继承类A、B)

注意:谁在前就先继承谁,如上则是先继承A再继承B

继承究竟做了什么?

继承继承,顾名思义就是继承了父类的成员(准确的说是会继承除去析构、构造以外所有的成员)再加上子类的扩展形成新类。

如下图:(请先忽略构造函数部分)子类student继承了父类people的_name和_age,所以这个子类中也有_name和_age成员变量。

继承的特性

继承方式

(父类XX成员以XX继承后,在子类是XX成员)

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

 总结:在继承方式和原本的成员权限中选权限更小的,如在父类中为public以protected继承则继承后的权限为protected(权限大小:public<protected<private)

虽然继承方式有三种,但常用的却只有public。

切片

在继承中,往往可以进行用子类对象直接调用父类函数的情况。像前者这种情况往往发生在相似类型身上,因为相似所以可以转换。而父类有的,子类都有,所以程序执行时就会把子类中与父类相同的部分拿出来去调用函数,这种情况就叫切片。

 

例如:将Student的对象s拿去给People的对象p去调用拷贝构造就是使用了切片的特性

子类构造与析构

虽说父类有的子类都有,理论上说构造自己搞定进行,但规定上在构造子类对象时要先调用父类构造函数,再构造子类部分。(继承是一种复用代码的机制,这里也是复用的父类构造,减少了工作量)

这就是为什么Student的构造函数中的初始化列表要调用  People(string name,int age)  这个构造(这个过程同样会发生切片)

构造要手动调用,那析构也要吗? 

手动调用时结果父类部分析构了两次。实际上在子类析构结束后会自动调用父类析构去释放子类中与父类一致的部分,不需要去手动调用。

隐藏(重定义)

当父类与子类出现同名的成员变量或成员函数时,子类成员将屏蔽对父类成员的直接访问,这时通过子类对象去访问这个同名成员时优先访问子类的。

class People
{
public:
	void printf()
	{
		cout << _name << " " << _age << endl;
	}

	People(string name, int age)
		:_name(name),
		_age(age)
	{}

	People(People& p)
		:_name(p._name),
		_age(p._age)
	{}

	~People()
	{
		cout << "~People()" << endl;
	}

	string _name;
	int _age;
};


class Student : public People 
{
public:
	void printf()
	{
		cout << _name << " " << _age << " "<< _Sno<<endl;
	}

	Student(string name, int age, int sno)
		:People(name, age),
		_Sno(sno)
	{}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	//学号
	int _Sno;
};


int main()
{
	Student s("swi",20,34);
	s.printf();
	return 0;
}

 之所以会出现隐藏这种特性与他的搜索逻辑有关,当我们通过子类对象访问成员时,会先去子类中寻找这个成员,如果没找到再去父类中寻找。


以上所讲都是继承的通用的特性,样例均为单继承

多继承

多继承顾名思义就是继承多个类。

class A
{
public:
	void printf()
	{
		cout << _a << endl;
	}

	int _a = 1;
};

class B
{
public:
	void printf()
	{
		cout << _b << endl;
	}

	int _b = 2;
};


//C同时继承了类A和类B(先继承的A,后继承的B)
class C:public A,B
{
public:
	void printf()
	{
		cout << _c << endl;
	}

	int _c = 3;
};


int main()
{
	C* c = new C;
	return 0;
}

查看内存后我们发现,缺省值为1的_a在缺省值为2的_b 的前面,class C:public A,B的顺序能够决定谁先被继承。

 A,B反过来后:

多继承的问题

 假设他们的成员变量分别为_a,_b,_c,_d,那么他们在内存中的存储会如下图:

此时就会出现两个问题:

  • 二义性:当我访问_a时,到底访问哪一个_a。
  • 数据冗余:出现重复数据,占用不必要空间 

解决方法

在继承方式前加上关键字virtual构成虚继承(注意:想要解决菱形继承的问题,放virtual关键字的类是出现重复部分的若干个子类,被virtual修饰的子类的父类称作虚基类

如下图的菱形问题(菱形问题是这类问题的总称,并不一定是菱形):

不处理时:

 使用virual处理后:

虚基类的成员将会在下方找到一片公共空间去存储,这样一来就解决了二义性和数据冗余。

但是原来两条红线存储的又是什么?

将这四行数字重新组合排序可以得到两个地址,去查找这个地址,发现他分别存储了一个十六进制的28,一个十六进制的18。实际上这个地址是虚基表的地址,而虚基表存储的是地址偏移量,该类的部分的起始地址加上偏移量就能找到虚基类的成员变量_a。

 

多态

对不同的对象会有不同的实现方法,即为多种形态。

多态的条件:

  1. 虚函数的重写(父子类虚函数需要三同,三同指函数名、参数、返回值)
  2. 父类的指针或引用去调用

 但是有三种例外:

  1. 协变(基类与派生类的虚函数返回值不同)
  2. 析构函数的重写(看似不符合函数名相同的条件,实际上编译器对其进行了特殊处理,编译后析构函数的名字统一处理成destructor)
  3. 派生类虚函数重写可以不加virtual(但建议写上)

虚函数

virtual修饰的函数叫做虚函数,虚函数只能是类中非静态的成员函数。

虚函数的重写

子类和父类中的虚函数拥有相同的名字,返回值,参数列表,那么称子类中的虚函数重写了父类的虚函数,或者叫做覆盖。(虚函数重写,重写的是函数体)

//成人
class People
{
public:
	virtual void fun()
	{
		cout << "全票" << endl;
	}
};

//儿童
class Child : public People 
{
public:
	virtual void fun()
	{
		cout <<"半票" << endl;
	}
};

void buyTicket(People& p)
{
	p.fun();
}

int main()
{
	People p;
	Child c;
	buyTicket(p);
	buyTicket(c);
	return 0;
}

多态的原理

为什么会访问到不同的虚函数? 

使用父类的指针或引用去调用(用子类来调用就会产生切片),根据其指针或引用可以找到其对应的虚函数表,进而找到虚函数地址然后去调用不同的虚函数。

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

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

相关文章

Laravel 6 - 第十三章 请求

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

SpringBoot引入第三方jar包或本地jar包

idea2018创建spring boot项目 New Project窗口选择Spring Initializr Type选择Maven(Generate…),有两个Maven选择这一个。 勾选Spring Web。 pom.xml中version改成2.5.10。 在resources中新建jar目录&#xff0c;将第三方jar包fastjson2-2.0.47.jar放入其中。&#xff08…

【C++】日期计算机

个人主页&#xff1a;救赎小恶魔 欢迎大家来到小恶魔频道 好久不见&#xff0c;甚是想念 今天我们要讲述的是一个日期类计算机的代码实现 引言&#xff1a; 我们日常生活中可能会有一个烦恼。 今天几月几号&#xff1f;过n天后又是几月几号&#xff1f;某年某月某天和x年…

Python读写文本URL蓝牙WIFI自动连接电子名片位置坐标智能海报等NDEF标签

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?id615391857885&spma1z10.5-c.w4002-21818769070.11.60ad789erlonvk 近场通信&#xff08;Near Field Communication&#xff0c;简称NFC&#xff09;&#xff0c;是一种新兴的技术&…

实验:使用apache + yum实现自制yum仓库

实验准备 Web服务器端&#xff1a;cenos-1&#xff08;IP&#xff1a;10.9.25.33&#xff09; 客户端&#xff1a;centos-2 保证两台机器网络畅通&#xff0c;原yum仓库可用&#xff0c;关闭防火墙和selinux Web服务器端 ①安装httpd并运行&#xff0c;设置开机自启动 安装…

【人工智能基础】人工神经网络

一、人工神经网络的三要素 人工神经元数理模型 MP模型是世界上第一个神经计算模型&#xff0c;为神经网络理论提供了基础 MP模型功能 对树突输入u的线性加权求和对净输入的非线性转换\ 作用函数的功能作用函数的功能 MP神经元模型的作用函数是单位阶跃函数。当x≥0时f(x)…

实现Node.js安装与配置。

一 、Node.js简介 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;用于构建高性能、可扩展的网络应用程序。它发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;允许使用JavaScript进行服务器端编程&#xff0c;使开发者能够在前后端都使用同一种编程…

ASP.NET教务平台—学籍管理模块开发与设计

摘 要 教务平台之学籍管理模块是一个典型的教务信息管理系统(MIS)&#xff0c;其开发主要包括后台数据库的建立和前端应用程序的开发两个方面。对于后台数据库要求实现数据的完整性、一致性和安全性&#xff1b;对于前台应用程序开发则要求模块功能完备、界面友好、易使用等特…

进程状态和优先级(进程第2篇)【Linux复习篇】

目录 一、进程状态 1、进程有什么状态&#xff1f; 2、 Linux下的进程状态有什么&#xff1f; 二、进程优先级 1、进程优先级是什么&#xff1f; 2、为什么要有优先级 3、怎么改进程优先级&#xff1f;要改吗&#xff1f; 4、操作系统如何根据优先级开展调度的&#xff…

Spring MVC和Spring Boot

上节已经提到过请求&#xff0c;这次梳理响应。 响应 响应基本上都要被Controller所托管&#xff0c;告诉Spring帮我们管理这个代码&#xff0c;我们在后面需要访问时&#xff0c;才可以进行访问&#xff0c;否则将会报错。并且其是由RestController分离出来的&#xff0c;Re…

【MCU】栈溢出问题

项目场景&#xff1a; 硬件&#xff1a;STM32F407&#xff0c;操作系统&#xff1a;rt_thread master分支 问题描述 问题栈溢出 id 499 ide 00 rtr 00 len 8 9 Function[rt_completion_wait] shall not be used in ISR (0) assertion failed at function:rt_completion_wait,…

MATLAB实现蚁群算法栅格路径优化

蚁群算法是一种模拟自然界中蚂蚁觅食行为的优化算法&#xff0c;常用于解决路径规划问题。在栅格路径优化中&#xff0c;蚁群算法可以帮助找到从起点到终点的最优路径。以下是蚁群算法栅格路径优化的基本流程步骤&#xff1a; 初始化参数&#xff1a; (1)设置蚂蚁数量&#xff…

【错题集-编程题】数组中的最长连续子序列(排序 + 模拟)

牛客对应链接&#xff1a;数组中的最长连续子序列_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 排序 模拟。 注意&#xff1a;值连续&#xff0c;位置可以不连续&#xff01;小心处理数字相同的情况。 二、代码 //值得学习的代码 class Solution { public:int MLS(vecto…

【数据库】Redis

文章目录 [toc]Redis终端操作进入Redis终端Redis服务测试切换仓库 String命令存储字符串普通存储设置存储过期时间批量存储 查询字符串查询单条批量查询 Key命令查询key查询所有根据key首字母查询判断key是否存在查询指定的key对应的value的类型 删除键值对 Hash命令存储hash查…

前端补充17(JS)

一、JS组成成分 JS的组成成分&#xff0c;由三部分组成 第一、ECMAScript&#xff1a;语法规则&#xff0c;如何定义变量&#xff0c;数据类型有哪些&#xff0c;如何转换数据类型&#xff0c;if判断 if-else while for for-in forEach do-while switch 数组 函数 对…

HTML表单(详解网页表单如何实现)

目录 一、表单介绍 1.概念 二、表单用法 1.HTML表单 2.HTML 表单 - 输入元素 2.1.文本域&#xff08;Text Fields&#xff09; 2.2.密码字段 2.3.单选按钮&#xff08;Radio Buttons&#xff09; 2.4.复选框&#xff08;Checkboxes&#xff09; 2.5.提交按钮(Submit)…

SAP 变更记录表查询使用逻辑简介

通常用户在遇到问题后&#xff0c;经常会问某个单据的变更记录&#xff0c;很多模块中在前台的操作界面中都根据对应的菜单栏中可以找到对应的变更记录&#xff0c;像销售订单、交货单、采购申请、采购订单都在菜单栏位中都可以查询到对应的修改记录&#xff0c;但是对于想批量…

“豪门”子刊!中科院2区SCI,收稿范围广,发文量超20000!无预警记录,极速录用见刊!

&#xff08;一&#xff09;期刊简介概况 【期刊类型】网络数据类SCIE 【出版社】SPRINGER出版社 【期刊概况】IF&#xff1a;4.0-5.0&#xff0c;JCR2区&#xff0c;中科院2区 【版面类型】正刊&#xff0c;仅10篇版面 【预警情况】2020-2024年无预警记录 【收录年份】2…

【项目】基于JDBC+MySQL的Java教务管理系统(附源码+论文说明)

摘要 随着信息技术的不断发展&#xff0c;教育管理也在向数字化、智能化方向迈进。Java作为一种广泛应用于企业级应用开发的编程语言&#xff0c;与数据库技术的结合更是为教务管理系统的开发提供了强大的支持。 本文将介绍基于JDBC&#xff08;Java Database Connectivity&a…

跨越未知,拥抱挑战——新征程

在浩瀚的IT领域里&#xff0c;每一位开发工程师都如同一位探险家&#xff0c;不断地探索、挑战和成长。作为一名新入职的Java开发工程师&#xff0c;我面临着全新的技术栈和业务领域&#xff0c;这是一次跨越未知的征程&#xff0c;也是一次自我提升的机会。 新入职 初入公司…