C++函数继承

引言

C++三大特征分别为封装,继承和多态,它们构成了面向对象编程的基石,它们协同工作以提升代码的模块化,可复用性和灵活性

  • 封装:提高代码的维护性(当程序出现问题时可以准确定位)

  • 继承:提高代码的复用性(在不做任何修改或者操作源码就能实现代码的复用)

  • 多态:提高代码的扩展性(后期文章会介绍)

组合

C++中实现代码复用操作主要有两种,分别是组合和继承,二者各有优缺点

#include <iostream>using namespace std;​class A{public:void func(){cout << "Hello world" << endl;}int m_num;};​class B{public:​//创建A类成员A a;​void func(){//此时B包含Aa.func();}};​int main(){B b;b.func();​return 0;}

执行结果:

上述代码就是一个典型的组合操作的实现,B包含A对象作为成员变量,这样B就可以调用A中的方法,此时修改A的内部逻辑并不会影响B(只要接口不变),操作时也更加直接,可以组合多个类,操作方法都是通过成员对象来访问,这样就在不复制代码内容的情况下使用了其他类中的功能。但这种方法每次组合一个类就要多创建一个成员,此时会增大内存占用。

继承

继承同样在代码复用方面起着重要作用

#include <iostream>using namespace std;​class A{public:void print(){cout << "Hello world" << endl;}int m_num;};​class B : public A{public:​//继承之后无需声明A类成员便可直接调用A类内的共有函数void func(){print();}};​int main(){A a;B b;b.func();​//继承了A类之后便可以直接使用与A相同的成员b.m_num = 10;b.print();​cout << b.m_num << endl;cout << a.m_num << endl;​cout << sizeof(b) << endl;​return 0;}​

输出结果

在这里与组合相比,继承就可以减少内存开销,继承之后B称为A的子类或派生类,A称为B的父类。派生类的实例化对象大小:父类的对象大小 + 派生类的新成员;与此同时我们还应该注意继承的空间分配问题,继承后派生类和父类并不公用内存,派生类会在继承后在自己的内存空间内开辟新的内存来存放与父类不同的成员,大致原理如下

根据程序输出结果也不难看出修改了B类成员后A类成员并没有一同发生改变

覆盖

#include <iostream>using namespace std;​class A{public:void print(){cout << "Hello world" << endl;}int m_num = 12;};​class B : public A{public://与父类重名的成员会在派生类中重新创建int m_num;int count;// 继承之后无需声明A类成员便可直接调用A类内的共有函数void func(){print();}​//与父类重名的函数会发生覆盖void print(){cout << "Hi world" << endl;}};​int main(){A a;B b;b.func();​b.m_num = 10;b.print();​cout << sizeof(b) << endl;​cout << b.m_num << endl;cout << a.m_num << endl;​return 0;}

输出结果

根据输出结果不难推理出派生类中的重名函数以及重名成员会覆盖子类中的对应函数和成员

三种继承方式

公有继承

基类的公有成员和属性成为派生类的共有;基类的被保护的属性和方法成为派生类的被保护;基类私有成员不能被继承

保护继承

基类的公有成员和属性成为派生类的私有;基类的被保护的属性和方法成为派生类的私有;基类私有成员不能被继承

私有继承

基类的公有成员和属性成为派生类的被保护的;基类的被保护的属性和方法成为派生类的被保护的;基类私有成员不能被继承

三种继承方式操作方法基本相同,只是权限控制不同,由于我们使用继承的出发点是实现代码复用,所以三种继承方式中使用最多的就是公有继承

继承后函数的执行顺序

#include <iostream>using namespace std;​class A{public:A(){cout << "A" << endl;}explicit A(int num) : m_num(num){cout << "A int" << endl;}​~A(){cout << "~A" << endl;}int m_num;};​class B : public A{public://使用初始化列表,调用A类的带参构造函数,如果不使用初始化列表则调用无参构造函数B() : A(5){cout << "B" << endl;}​~B(){cout << "~B" << endl;}​int m_count;};​int main(){B b;cout << b.m_num << endl;return 0;}

输出结果

由输出结果不难看出,继承之后构造函数的执行顺序为先调用基类的构造函数然后再派生类的构造函数,析构函数则相反。

同时派生类继承的属性需要基类的构造函数进行初始化;如果没有无参构造函数,那么派生类里所有的构造函数都要显示调用基类的构造函数

子对象的构造顺序

当一个类中声明而多个子对象时它们的构造函数调用顺序又是怎样的呢

#include <iostream>using namespace std;​class Test{public:​Test(int index) : m_index(index){cout << "Test int" << endl;}​~Test(){cout << "~Test" << endl;}​int m_index;};​class Test1{public:​Test1(int index) : m_index(index){cout << "Test1 int" << endl;}​~Test1(){cout << "~Test1" << endl;}​int m_index;};​class B{public://通过初始化列表来初始化对象//子对象在初始化列表中的顺序B() : t1(0),t(0){cout << "B" << endl;}​~B(){cout << "~B" << endl;}​int m_count;​//子对象的声明顺序Test t;Test1 t1;};​int main(){B b;​return 0;}

输出结果

根据上述代码可以推出子对象构造函数的调用顺序与子对象的声明顺序相关。先声明的对象先构造

多基类的构造顺序

#include <iostream>using namespace std;​class A{public:​A(int index) : m_index(index){cout << "A int" << endl;}​~A(){cout << "~A" << endl;}​int m_index;};​class B{public:​B(int index) : m_index(index){cout << "B int" << endl;}​~B(){cout << "~B" << endl;}​int m_index;};​//继承顺序class C : public A, public B{public://通过初始化列表来初始化对象//列表顺序C() : B(0),A(0){cout << "C" << endl;}​~C(){cout << "~B" << endl;}​int m_count;​};​int main(){C c;​return 0;}

输出结果

根据输出结果不难看出当继承了多个基类时基类的构造顺序由继承顺序决定

二义性问题

多继承会产生二义性问题,具体形式如下、

直接指定路径

当我们想要在D类内访问a时编译器就会面临两个选择,要么通过类B访问,要么通过类C访问,此时就会产生多继承问题

代码实现如下

#include <iostream>​using namespace std;//基类Aclass A{public:A(int a) : m_a(a){cout << "A" << endl;}int m_a;};​//基类B继承Aclass B : public A{public:B(int b) : m_b(b),A(1){cout << "B" << endl;}int m_b;};​//基类C继承Aclass C : public A{public:C(int c) : m_c(c),A(2){cout << "C" << endl;}int m_c;};​//派生类D同时继承B和Cclass D : public B,public C{public:D(int d) : m_d(d), B(2), C(3){cout << "D" << endl;}int m_d;};​int main(){D d(4);​//引发报错,二义性问题导致编译器我无法确定访问路径cout << d.m_a << endl;return 0;}

输出结果:

此时解决问题的方法由两种,一种是直接指明路径将输出语句内容更换为cout << d.C::m_a << endl;,此时程序就能正常输出

这种直接指定路径的方法就可以解决二义性的问题,但是通过输出结果我们发现A被构造了两次,因此相比于这种方法我们会倾向于选择使用虚继承来解决问题

虚继承

#include <iostream>​using namespace std;class A{public:A(int a) : m_a(a){cout << "A" << endl;}int m_a;};​//此时B和C采用虚继承class B : virtual public A{public:B(int b) : m_b(b),A(1){cout << "B" << endl;}int m_b;};​class C : virtual public A{public:C(int c) : m_c(c),A(2){cout << "C" << endl;}int m_c;};​class D : public B,public C{public:D(int d) : m_d(d), B(2), C(3), A(1){cout << "D" << endl;}int m_d;};​int main(){D d(4);​cout << d.C::m_a << endl;return 0;}

输出结果

此时我们通过观察发现程序中A类只被构造了一次,在D类中实现的构造,同时需要注意的是当使用虚继承时B和C都要采用虚继承,如果只有一个采用了虚继承那么还是会引发报错,因为此时A被构造了两次,又产生了二义性问题

组合和继承的选择

当两者之间是has-a(例如:学生有书包,汽车有发动机)关系时通常使用组合方式,当两者关系为is-a关系(例如:老师是人,学生也是人)时使用继承方式

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

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

相关文章

瞬态数据表定义Fluent变量

重要说明&#xff1a;本文基于2025R2版本编写&#xff0c;其他版本可能存在差异。1 概述瞬态数据表是定义 Fluent 变量随时间变化规律的profile文件&#xff0c;文件类型为文本文件。瞬态数据表假设所有时刻&#xff0c;被定义的对象都是均匀分布&#xff0c;无法考虑变量在空间…

文本嵌入模型的本质

这是一个非常深刻且重要的问题。我们来详细拆解一下“通用文本嵌入模型”的本质。 我们可以从三个层次来理解它&#xff1a;它是什么&#xff08;What&#xff09;&#xff0c;它如何工作&#xff08;How&#xff09;&#xff0c;以及它为什么重要&#xff08;Why&#xff09;。…

Linux笔记13——shell编程基础-7

补充1.printf %s\t%s 字符串 中&#xff0c;\t一定不要加双引号&#xff0c;这一点和在awk中使用的时候有所不同2.其中%s也可以写成%ns&#xff0c;n可以被用来设置列宽&#xff0c;默认右对齐#打印输出文件系统的使用情况 [rootlocalhost ~]# printf %-30s\t%s\n $(df -h | aw…

【混合开发】Android+WebView视频图片播放硬件加速详解

webview视频播放出现白屏、蓝屏、花屏、黑屏等等 但由于布局结构是androidwebviewH5本地视频等。视频播放导致的异常排查起来十分复杂且没有原生的相关日志 于是需要给webview播放视频进行硬件加速&#xff0c;刚开始以为是一件很简单的配置而已。本着无经验从头开始的原则&am…

Allegro-DDR3实战-差分对-等长设置-区域规则

本章内容&#xff1a; 一&#xff09;Allegro之DDR3设计 (实操干货) 二&#xff09;规则设置具体步骤 DDR3信号表: (eg&#xff1a;镁光MT41J256M16HA-15E) 数据信号 DQ[15:0] DQS[1:0] DM[1:0] DQ:双向数据总线 DQS:数据选通&#xff0c;用于同步数据传…

七牛云OSS空间复制迁移到另外一个空间

创新新的空间时存储地区必须一致 访问控制必须选择公开 1、下载七牛的同步工具并解压 qshell&#xff08;http://developer.qiniu.com/docs/v6/tools/qshell.html&#xff09; 2、解压文件 3、运行cmd登录到七牛账号 qshell account 你的七牛AK 你的七牛SK 你的账号 4、测…

windows中Qwen3‑Coder 与 Claude Code 搭配使用

claude安装命令 npm install -g anthropic-ai/claude-code环境变量配置 set ANTHROPIC_BASE_URLhttps://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy set ANTHROPIC_AUTH_TOKENyour-dashscope-apikey可能还需要配置自己的git环境变量 查看git安装位置 按下Win S打…

thunar 文件管理器实现双击使用 nvim打开

archlinux 中thunar 文件管理器&#xff0c;如何实现双击使用 nvim打开查看。我用的是kitty 终端。 在 Arch Linux Thunar kitty nvim 的环境里&#xff0c;要实现 双击文件 -> 用 nvim 打开&#xff0c;你可以这样配置&#xff1a;设置为默认应用 如果你想 双击直接用 n…

深度学习----卷积神经网络实现数字识别

一、准备工作 导入库&#xff0c;导入数据集&#xff0c;划分训练批次数量&#xff0c;规定训练硬件&#xff08;这部分 import torch from torch import nn # 导入神经网络模块 from torch.utils.data import DataLoader # 数据包管理工具&#xff0c;打包数据 from torch…

鸿蒙Harmony-从零开始构建类似于安卓GreenDao的ORM数据库(四)

目录 一,查询表的所有数据 二,根据条件查询数据 三,数据库升级 前面章节已经讲解了数据库的创建,表的创建,已经增删改等操作。下面我们来讲解一下数据库的查询以及升级操作。 一,查询表的所有数据 先来看看官方文档: query(predicates: RdbPredicates, callback: Asy…

20250829_编写10.1.11.213MySQL8.0异地备份传输脚本+在服务器上创建cron任务+测试成功

0.已知前提条件: 10.1.11.213 堡垒机访问 mysql 8.0 版本 密码在/root/.my.cnf 备份脚本:/data/backup_mysql/mysql_backup.sh alarm_system:动环数据库 exit_and_entry:出入境数据库 logs:备份日志 project_cg_view_prod:采购跟踪系统 all :数据库整体备份 imip_ecb…

PostgreSQL 流复制与逻辑复制性能优化与故障切换实战经验分享

PostgreSQL 流复制与逻辑复制性能优化与故障切换实战经验分享 在高可用和数据安全愈发受到重视的生产环境中&#xff0c;PostgreSQL 复制技术是保障业务连续性的重要手段。本文结合真实生产场景&#xff0c;分享流复制&#xff08;Physical Replication&#xff09;与逻辑复制&…

Django开发规范:构建可维护的AWS资源管理应用

引言 在现代Web开发中,遵循一致的开发规范对于项目的可维护性和团队协作至关重要。本文基于实际的AWS资源管理项目,分享一套经过实践检验的Django开发规范,涵盖模型设计、Admin配置、管理命令和工具类开发等方面。 模型开发规范 数据模型设计原则 良好的数据模型设计是应…

机器学习可解释库Shapash的快速使用教程(五)

文章目录1 快速使用1.1 安装1.2 三个简单步骤快速入门1.2.1 步骤 1&#xff1a;准备模型和数据1.2.2 步骤 2&#xff1a;声明并编译 SmartExplainer1.2.3 步骤 3&#xff1a;可视化和探索1.2.4 启动 Web 应用1.2.5 将解释结果导出为数据2 Shapash的后端集成2.1 方法一&#xff…

如何在emacs中添加imenu插件

在配置文件中添加&#xff1a; ;; 删除现有的包管理器配置&#xff08;如果有&#xff09;&#xff0c;然后添加以下&#xff1a;;; 初始化包管理器 (require package);; 清除现有的仓库列表 (setq package-archives nil);; 添加正确的仓库&#xff08;注意&#xff1a;使用 H…

Linux下的网络编程SQLITE3详解

常用数据库关系型数据库将复杂的数据结构简化为二维表格形式大型&#xff1a;Oracle、DB2中型&#xff1a;MySql、SQLServer小型&#xff1a;Sqlite非关系型数据库以键值对存储&#xff0c;且结构不固定JSONRedisMongoDBsqlite数据库特点开源免费&#xff0c;C语言开发代码量少…

适配openai

openai 脚本 stream脚本import os from openai import OpenAIclient OpenAI(base_url"http://127.0.0.1:9117/api/v1",api_keyos.environ["ACCESS_TOKEN"], )stream client.chat.completions.create(model "Qwen/Qwen2-7B-Instruct",messages…

一天认识一个神经网络之--CNN卷积神经网络

CNN 是一种非常强大的深度学习模型&#xff0c;尤其擅长处理像图片这样的网格结构数据。你可以把它想象成一个系统&#xff0c;它能像我们的大脑一样&#xff0c;自动从图片中学习并识别出各种特征&#xff0c;比如边缘、角落、纹理&#xff0c;甚至是更复杂的物体部分&#xf…

13 SQL进阶-InnoDB引擎(8.23)

一、逻辑存储结构&#xff08;1&#xff09;表空间&#xff08;ibd文件&#xff09;&#xff1a;一个mysql实例可以对应多个表空间&#xff0c;用于存储记录、索引等数据。cd /var/lib/mysql&#xff08;2&#xff09;段&#xff0c;分为数据段&#xff08;leaf node segment&a…

MTK Linux DRM分析(二十四)- MTK mtk_drm_plane.c

一、代码分析 mtk_drm_plane.h 和 mtk_drm_plane.c 两个文件,并生成基于文本的函数调用图,我将首先解析文件中的主要函数及其功能,然后根据代码中的调用关系整理出调用图。由于文件内容较长,我会专注于关键函数及其相互调用关系,并以清晰的文本形式呈现。 文件分析 1. …