c++ 继承与派生的简单理解

继承与派生概念:

派生是一种创建新类的方式,在原来已有被继承类的基础上,不影响原来的类,不改变原来类的代码,实现对于功能的扩展,在原有被继承类的基础上快速增加新的功能;新创建的类可以来源于一个类或多个类,即新类可以继承一个或多个类;

继承描述的是类与类之间的关系,新创建的类被称为派生类或子类,被继承的类称为基类或父类;

通常使用父类->子类或基类->派生类这两种固定组合保持风格统一,不要使用父类->派生类和基类->子类这2种组合,不便于阅读;

继承分为单继承和多继承,其语法格式如下:

单继承

class 基类名

{

};

class 派生类名 : 继承方式(权限) 基类名

{

};

多继承

class 基类名1

{

};

class 基类名2

{

};

class 基类名n

{

};

class 派生类名 : 继承方式1 基类名1, 继承方式2 基类名2, 继承方式n 基类名n

{

};

继承权限:

1.共有继承public

2.私有继承private

3.保护继承protected

在 C++ 中,protected 是一种访问控制修饰符,它用于指定类中的成员属性或方法的访问权限。使用 protected 来修饰的成员可以被其所在类及其派生类的成员访问,但是在类外部是不可以访问的。

具体来说,使用 protected 修饰的成员在类外部是不能直接访问的,但是可以通过其派生类来访问。也就是说,在派生类中可以访问基类的 protected 成员。而在基类中,其他类无法访问基类的 protected 成员,只能够访问 public 成员。

总之,protected 成员可以被派生类访问,是为了方便派生类继承并重用基类的代码。它可以使得基类的一些属性和方法只对其子类可见,保证了类的封装性和安全性。

注意:继承是子类继承父类全部的成员(即所有内存空间),但是访问权限要根据父类的成员属性和继承方式共同决定,见下文:

共有继承public

私有继承private

保护继承proteed

父类public成员

子类可以获得public访问权限

子类可以获得private访问权限

子类可以获得protected访问权限

父类private成员

子类可以获得不可访问

子类可以获得不可访问

子类可以获得不可访问

父类protected成员

子类可以获得protected访问权限

子类可以获得private访问权限

子类可以获得protected访问权限

规律:

父类的公有成员被子类继承后,子类对父类拥有成员的访问权限根据子类的继承方式设置与继承方式相同的访问权限;

父类的私有成员被子类继承后,不管子类用什么方式去继承都不可访问;

父类的保护成员被子类继承继承后,按照父类成员访问权限与子类继承权限种两者最严格的方式设置访问权限;

多级继承:分析直接父子类就可以,因为继承过程种数据不会丢失。

// 继承与派生该概念以及访问权限
#include <iostream>
#include <string>using namespace std;class Father {
public:Father() {};~Father() {};public:int pub;void pub_func() {};
private:int pri;void pri_func() {};
protected:// 在 C++ 中,protected 是一种访问控制修饰符,它用于指定类中的成员属性或方法的访问权限。// 使用protected修饰的成员可以被其所在类及其派生类的成员访问,但是在类外部是不可以访问的。// 具体来说,使用protected修饰的成员在类外部是不能直接访问的,但是可以通过其派生类来访问。// 也就是说,在派生类中可以访问基类的protected成员。而在基类中,其他类无法访问基类的protected成员,只能够访问public成员。// 总之,protected成员可以被派生类访问,是为了方便派生类继承并重用基类的代码。// 它可以使得基类的一些属性和方法只对其子类可见,保证了类的封装性和安全性。int pro;void pro_func() {};
};class Father1 {
public:Father1() {};~Father1() {};
public:int pub;void pub_func() {};
private:int pri;int pri1;void pri_func() {};
protected:int pro;void pro_func() {};
};// 最基本的继承关系写法
class Son1 : public Father {
public:Son1() {};~Son1() {};
public:
private:
protected:
};// 最基本的继承关系写法
class Son2 : public Father {
public:Son2() {};~Son2() {};
public:int num;void testFunc() {// 继承后访问权限测试// 父类的public成员可以在类里边和类外访问this->pub;this->pub_func();// 父类的protected成员可以在继承类里边访问,但是不能在类外访问this->pro;this->pro_func();// 父类的private成员不能访问// this->pri;// this->pri_func();}
private:
protected:
};class Son3 : public Father, public Father1
{};int main()
{cout << "sizoef(Father) = " << sizeof(Father) << endl;// 子类Son1继承了父类的一切,但是没有增加新的成员,所以内存大小与父类一致cout << "sizoef(Son1) = " << sizeof(Son1) << endl;// 子类Son2继承了父类的一切,自己有增加新的东西,内存大小在父类基础上增加了新增成员的大小cout << "sizoef(Son2) = " << sizeof(Son2) << endl;// 子类Son2继承了2个父类的一切,内存大小为2个父类之和cout << "sizeof(Son3) = " << sizeof(Son3) << endl;Son2 obj_son2;// public方式继承的子类对象可以访问父类public成员obj_son2.pub;obj_son2.pub_func();// public方式继承的子类对象不可以访问父类private成员// obj_son2.pri;// obj_son2.pri_func();// public方式继承的子类对象不可以在类外访问父类protected成员// obj_son2.pro;// obj_son2.pro_func();return 0;
}

继承与派生类的关系:

1.派生类的构成

构造(包括拷贝构造)和析构不会继承,因为任何类都默认会有构造和析构函数,其他的都会继承;

2.父类不会调用子类新增的成员;

3.如果子类新增成员名称和父类已有成员相同,那么子类会将父类成员隐藏,使用父类成员时通过对应父类的类名加作用域运算符的形式访问,如果子类有多个父类时访问方式同理;

子类是父类的对象,但是父类不是子类的对象;

也就是说派生类对象可以当作基类对象使用,因为派生类继承了基类的所有成员,基类有的派生类都有,直接用派生类就可以,即可以用父类的地方就可以用子类;

// 继承类与基类之间的成员访问关系
// 1.派生类的构成
// 构造(包括拷贝构造)和析构不会继承,因为任何类都默认会有构造和析构函数,其他的都会继承;
// 2.父类不会调用子类新增的成员;
// 3.如果子类新增成员名称和父类已有成员相同,那么子类会将父类成员隐藏,
// 使用父类成员时通过对应父类的类名加作用域运算符的形式访问,如果子类有多个父类时访问方式同理;
#include <iostream>
#include <string>using namespace std;class Father {
public:Father() {val = 888;num = 889;};~Father() {};
public:int val;int num;
private:
protected:
};class Mother {
public:Mother() {val = 666;num = 667;};~Mother() {};
public:int val;int num;
private:
protected:
};class Son1 : public Father
{
public:Son1() {num = 111;};~Son1() {};
public:int num;
private:
protected:
};class Son2 : public Father, public Mother
{
public:Son2() {num = 222;};~Son2() {};
public:int num;
private:
protected:
};int main()
{Son1 obj_son1;// 子类对象已经将父类的同名成员隐藏,子类直接访问时只能访问自己新增的同名成员cout << "obj_son1.num = " << obj_son1.num << endl;// 子类需要访问父类同名成员时需要加父类类名加作用域运算符加同名成员进行访问cout << "obj_son1.Fater::num = " << obj_son1.Father::num << endl;Son2 obj_son2;cout << "obj_son2.num = " << obj_son2.num << endl;// 子类继承自多个父类时,需要访问某个父类同名成员时需要加对应父类类名加作用域运算符加同名成员进行访问cout << "obj_son2.Fater::num = " << obj_son2.Father::num << endl;cout << "obj_son2.Mother::num = " << obj_son2.Mother::num << endl;return 0;
}
// 派生类与基类之间的关系
// 子类是父类的对象,但是父类不是子类的对象;
// 也就是说派生类对象可以当作基类对象使用,因为派生类继承了基类的所有成员,
// 基类有的派生类都有,直接用派生类就可以,即可以用父类的地方就可以用子类。
#include <iostream>
#include <string>using namespace std;class Father
{
public:Father() {};~Father() {};
private:
protected:
};class Son : public Father
{
public:Son() {};~Son() {};
};int main()
{Father obj_father;Son obj_son;// 子类对象可以给父类对象赋obj_father = obj_son;// 但是父类对象不能给子类对象赋值// obj_son = obj_father;Father *p_father;Son *p_son;// 父类指针指向父类对象okp_father = &obj_father;// 父类指针指向子类对象okp_father = &obj_son;// 子类指针指向子类对象okp_son = &obj_son;// 子类指针不可以指向父类对象// p_son = &obj_father;return 0;
}

基类与派生类构造析构顺序

// 继承类与父类之间构造与析构调用顺序
// 如果要创建子类对象就要先调用父类的构造函数
// 父类带参构造以及父类构造传参#include <iostream>
#include <string>
using namespace std;class Father {
public:// 没有参数默认val为设定值111Father() : val(111) {cout << "父类构造" << endl;};// 父类的带参构造, 将val的值设置为输入参数n// 由于构造函数不能被主动调用,由系统自动调用,需要使用成员初始化列表进行构造函数传参Father(int n) : val(n) {cout << "父类的带参构造" << endl;};~Father() {cout << "父类析构" << endl;};
public:const int val;
private:
protected:
};class Son : public Father 
{
public:Son() {num = 666;cout << "子类构造" << endl;};// 子类的带参构造,由于构造函数不能被主动调用,由系统自动调用,所以需要使用成员初始化列表进行构造函数传参Son(int n, int v) : Father(v), num(n) {// 以下这种做法不是构造函数传参,而是创建了一个无名的Father对象// Father(v);cout << "子类的带参构造" << endl;}~Son() {cout << "子类析构" << endl;};
public:int num;
private:
protected:
};int main()
{Son obj_son;cout << "obj_son.val = " << obj_son.val << ", obj_son.num = " << obj_son.num << endl;// 创建子类对象时,先调用父类构造再调用子类构造// 销毁对象时,像调用子类析构在调用父类析构// 遵循的原则是:先构造的后析构// 执行程序后打印结果如下// 父类构造// 子类构造// obj_son.val = 111, obj_son.num = 666// 子类析构// 父类析构Son obj_son1(3, 4);cout << "obj_son1.val = " << obj_son1.val << ", obj_son.num = " << obj_son1.num << endl;return 0;
}

棱形继承

// 棱形继承
// 棱形继承是特殊的情况,也是很容易出现问题的一种情况
// 棱形继承的情况下,最终子类中只会有一份来自父类的内存
#include <iostream>
#include <string>using namespace std;class A
{
public:int num;
};class A1 : public A
{
public:int a1;
};class A2 : public A
{
public:int a2;
};// 棱形继承
class AA : public A1, public A2
{
public:int aa;
};// 在这里添加virtual描述,直接使用A3A4时不会产生任何影响
// virtual描述的虚继承通过虚指针标记A3A4从基类A中继承了那些成员
class A3 : virtual public A
{
public:int a3;
};class A4 : virtual public A
{
public:int a4;
};// A3A4通过virtual修饰,虚指针已经标记该类继承了基类A中的哪些成员
// AB使用棱形继承的方式继承A时只会得到一份A类的成员,不会造成重复继承导致内存浪费
// 使用虚继承后A3A4相比普通继承方式A1A2会多出4个字节,这4个字节用于存放虚指针,
// 在类A数据量较大时使用只有4字节的虚指针方式可以避免重复继承类A成员内存开销减小
// AB中也会有一个同样的虚指针
// 棱形继承的情况下,最终子类中只会有一份来自父类的内存
class AB : public A3, public A4
{
public:int ab;
};int main()
{AA obj_aa;// 访问权限obj_aa.aa;obj_aa.a1;obj_aa.a2;// AA没有直接继承A,不是AA的直接父类,所以无法直接访问A类中的成员// obj_aa.num;// aa继承了A1和A2,可以通过这2个直接父类访问A1和A2继承的基类A中的成员obj_aa.A1::num;obj_aa.A2::num;// aa没有直接继承A,所以无法直接访问A类中的成员// obj_aa.A::num;// virtual虚继承方式AB obj_ab;obj_ab.A3::num;obj_ab.A4::num;// 通过虚继承方式可以不需要再使用类名进行限定,指定访问类A中的成员obj_ab.num;obj_ab.A3::a3;obj_ab.A4::a4;obj_ab.a3;obj_ab.a4;return 0;
}

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

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

相关文章

第四篇:记忆的迷宫:探索计算机存储结构的奥秘与创新

记忆的迷宫&#xff1a;探索计算机存储结构的奥秘与创新 1 引言 1.1 计算机存储系统的发展与重要性 在现代计算技术中&#xff0c;存储系统承担着非常关键的角色&#xff0c;它不仅负责信息的持久保存&#xff0c;同时确保高效的数据访问速度&#xff0c;影响着整体系统性能的…

《Fundamentals of Power Electronics》——基础交流建模方法

PWM整流器小信号交流模型建模的主要步骤为&#xff1a; (a)利用小纹波近似的动态版本&#xff0c;建立与电感和电容波形的低频平均值有关的方程&#xff1b; (b)平均方程的扰动和线性化&#xff1b; (c)交流等效电路模型的建立。 以下图buck-boost电路为例进行分析。 首先测…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑碳捕集和电转气的综合能源系统优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

SpringBoot与SpringMVC的区别

SpringBoot与SpringMVC的区别是什么&#xff1f; SpringBoot和SpringMVC是Java开发中常用的两个框架&#xff0c;它们都是由Spring框架所提供的&#xff0c;但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。 一、SpringBoot的特点&#…

Qt服务器端与客户端交互

Qt做客户端与服务器端交互第一步引入network 第一步引入network后继续编程首先界面设计 创建server和socket 引入QTcpServer&#xff0c;QTcpSocket MainWindow.h代码如下 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QTcpServer&…

13_Qt中的快捷键

Qt Creator的一些快捷操作&#xff1a; 项目管理&#xff1a; Build&#xff1a;以增量方式构建项目。Rebuild&#xff1a;重新构建项目。Clearn&#xff1a;清除项目构建过程中产生的所有中间文件。Run qmake&#xff1a;使用qmake/cmake重新构建项目。会重新执行UIC、MOC、…

如何面对并发下的bug

整理总结自蒋炎岩老师的b站课程&#xff0c;https://jyywiki.cn/OS/2022/index.html 并发bug与应对 应对bug的方法 在代码里边增加很多检查(加断言) #include "thread.h"unsigned long balance 100;void Alipay_withdraw(int amt) {if (balance > amt) {usleep(…

什么是网络安全和网络隐私?

什么是网络安全?这个是我最感兴趣的话题,网络安全说白了就是在网络上的安全,跟现实中一样,现实中为了家里的安全,我们会给家门上锁,会装监控,农村的话可能还会养一条狗,只有我们让别人进我们家,别人才能进来,对于计算机来说也是一样的,我们会设置账户的密码,会设置防火墙,会安…

迎接AI时代:智能科技的社会责任与未来展望

AI智能体的社会角色、伦理挑战与可持续发展路径 引言&#xff1a; 在技术的浪潮中&#xff0c;AI智能体正逐步成为我们生活的一部分。它们在医疗、教育、交通等领域的应用&#xff0c;预示着一个全新的时代即将到来。本文将结合实际案例和数据分析&#xff0c;深入探讨AI智能体…

农作物害虫分类数据集12846张27类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;12846 分类类别数&#xff1a;27 类别名称:["ants","aphids…

Cisco WLC 2504控制器重启后所有AP掉线故障-系统日期时间

1 故障描述 现场1台WLC 2504控制器掉电重启后&#xff0c;所有AP均无线上线&#xff0c; 正常时共有18个AP在线&#xff0c;而当前为0 AP在线数量为0 (Cisco Controller) >show ap sumNumber of APs.................................... 0Global AP User Name..........…

国内各种免费AI聊天机器人(ChatGPT)推荐(中)

作者主页&#xff1a;点击&#xff01; 国内免费AI推荐(ChatGPT)专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月29日15点20分 随着人工智能技术的不断发展&#xff0c;AI聊天机器人已经逐渐融入我们的日常生活。它们可以提供各种服务&#xff0c;例如聊天、…

关于win平台c语言引入开源库的问题与解决

许久不写博客&#xff0c;五一还在加班&#xff0c;就浅浅写一篇吧 最近除了做物联网平台 还对网关二次开发程序做了修改&#xff0c;网关的二次开发去年年底的时候做过&#xff0c;但是当时的逻辑不是十分完善&#xff0c;差不多已经过了半年了&#xff0c;很多细节已经忘记了…

一毛钱不到的FH8208C单节锂离子和锂聚合物电池一体保护芯片

前言 目前市场上电池保护板&#xff0c;多为分体方案&#xff0c;多数场合使用没有问题&#xff0c;部分场合对空间有进一步要求&#xff0c;或者你不想用那么多器件&#xff0c;想精简一些&#xff0c;那么这个芯片就很合适&#xff0c;对于充电电池来说&#xff0c;应在使用…

foobar2000 for Mac:卓越音乐播放器

当您在寻找一款音质卓越、功能丰富的音频播放器时&#xff0c;foobar2000 for Mac无疑是您的首选。它拥有简洁明了的界面设计&#xff0c;易于上手&#xff0c;同时支持多种音频格式&#xff0c;让您无需担心兼容性问题。 foobar2000 for Mac v2.6.4免激活版下载 foobar2000 fo…

UG NX二次开发(C++)-UFun函数-UF_MODL_check_interference(干涉检查)

UFun函数目录 例如:第一章 Python 机器学习入门之pandas的使用 文章目录 UFun函数目录1、前言2、UF_MODL_check_interference的函数意义3、UF_MODL_check_interference的函数输入与输出4、测试案例4.1 测试案例内容说明4.2 测试用代码为:4.3 测试结果1、前言 `在UG NX二次开…

AIGC在汽车软件开发的应用举例

AIGC(人工智能生成内容)在汽车软件开发领域的应用主要体现在以下几个方面: 个性化和定制化车辆的创造:通过分析大型数据集并生成新内容,AIGC技术使得创造更加个性化和定制化的车辆成为可能。这不仅提高了车辆的安全性和用户体验,还推动了自动驾驶汽车的发展。 智能座舱…

3.3Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3框架-企业级应用-Vue组合式API

为什么要使用Composition API 一个Options API实例 在前面的课程中&#xff0c;我们都是采用 Options API&#xff08;基于选项的 API &#xff09; 来写一个组件的。下面是一个实例&#xff1a; <template> Count is: {{ count }}, doubleCount is: {{ doubleCount…

Python数据分析案例44——基于模态分解和深度学习的电负荷量预测(VMD+BiGRU+注意力)

案例背景 承接之前的案例&#xff0c;说要做模态分解加神经网络的模型的&#xff0c;前面纯神经网络的缝合模型参考数据分析案例41和数据分析案例42。 虽然我自己基于各种循环神经网络做时间序列的预测已经做烂了.....但是还是会有很多刚读研究生或者是别的领域过来的小白来问…

Elasticsearch 数据聚合

Bucket聚合&#xff08;桶聚合&#xff09; 对文档做分组&#xff0c;aggs 按照文档字段值或日期进行分组&#xff0c;能参与分词的字段不能做聚合&#xff0c;如text类型的字段 例如&#xff1a;根据城市名称做聚合&#xff0c;也就是城市名称对数据进行分组统计。可以加qu…