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;
    // 父类指针指向父类对象ok
    p_father = &obj_father;
    // 父类指针指向子类对象ok
    p_father = &obj_son;
    // 子类指针指向子类对象ok
    p_son = &obj_son;
    // 子类指针不可以指向父类对象
    // p_son = &obj_father;

    return 0;
}

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

// 继承类与父类之间构造与析构调用顺序
// 如果要创建子类对象就要先调用父类的构造函数
// 父类带参构造以及父类构造传参

#include <iostream>
#include <string>
using namespace std;

class Father {
public:
    // 没有参数默认val为设定值111
    Father() : 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.mfbz.cn/a/592679.html

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

相关文章

Llama改进之——SwiGLU激活函数

引言 今天介绍LLAMA模型引入的关于激活函数的改进——SwiGLU1&#xff0c;该激活函数取得了不错的效果&#xff0c;得到了广泛地应用。 SwiGLU是GLU的一种变体&#xff0c;其中包含了GLU和Swish激活函数。 GLU GLU(Gated Linear Units,门控线性单元)2引入了两个不同的线性层…

Linux(openEuler、CentOS8)常用的IP修改方式(文本配置工具nmtui+配置文件+nmcli命令)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS类似&#xff0c;可参考本文&#xff09;---- 一、知识点 &#xff08;一&#xff09;文本配置工具nmtui(openEuler已预装) nmtui&#xff08;NetworkManager Text User Interface&#xff09;是一…

ZooKeeper以及DolphinScheduler的用法

目录 一、ZooKeeper的介绍 数据模型 ​编辑 操作使用 ①登录客户端 ​编辑 ②可以查看下面节点有哪些 ③创建新的节点&#xff0c;并指定数据 ④查看节点内的数据 ⑤、删除节点及数据 特殊点&#xff1a; 运行机制&#xff1a; 二、DolphinScheduler的介绍 架构&#…

计算机毕业设计Python+Spark知识图谱高考志愿推荐系统 高考数据分析 高考可视化 高考大数据 大数据毕业设计

毕业设计&#xff08;论文&#xff09;任务书 毕业设计&#xff08;论文&#xff09;题目&#xff1a; 基于大数据的高考志愿推荐系统 设计&#xff08;论文&#xff09;的主要内容与要求&#xff1a; 主要内容&#xff1a; 高…

贝叶斯回归

1. 贝叶斯推断的定义 简单来说&#xff0c;贝叶斯推断 (Bayesian inference) 就是结合“经验 (先验)”和“实践 (样本)”&#xff0c;得出“结论 (后 验)”。 2. 什么是先验&#xff1f; 贝叶斯推断把模型参数看作随机变量。在得到样本之前&#xff0c;根据主观经验和既有知…

巧记英语单词

页面 在输入框中填写英语单词的谐音 这样的话就进行了一次英语单词的记忆练习。 页面代码 <% layout(/layouts/default.html, {title: 英语单词管理, libs: [dataGrid]}){ %> <div class"main-content"><div class"box box-main">&l…

anaconda、cuda、tensorflow、pycharm环境安装

anaconda、cuda、tensorflow、pycharm环境安装 anaconda安装 anaconda官方下载地址 本文使用的是基于python3.9的anaconda 接下来跟着步骤安装&#xff1a; 检验conda是否成功安装 安装CUDA和cuDNN 提醒&#xff0c;CUDA和cuDNN两者必须版本对应&#xff0c;否者将会出错…

my-room-in-3d中的电脑,电视,桌面光带发光原理

1. my-room-in-3d中的电脑&#xff0c;电视&#xff0c;桌面光带发光原理 最近在github中&#xff0c;看到了这样的一个项目&#xff1b; 项目地址 我看到的时候&#xff0c;蛮好奇他这个光带时怎么做的。 最后发现&#xff0c;他是通过&#xff0c;加载一个 lightMap.jpg这个…

大型语言模型的新挑战:AMR语义表示的神秘力量

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享&#xff0c;与你一起了解前沿科技知识&#xff01; 引言&#xff1a;AMR在大型语言模型中的作用 在自然语言处理&#xff08;NLP&#xff09;的领域中&#xff0c;抽象意义表示&…

查找算法与排序算法

查找算法 二分查找 (要求熟练) // C// 二分查找法&#xff08;递归实现&#xff09; int binarySearch(int *nums, int target, int left, int right) // left代表左边界&#xff0c;right代表右边界 {if (left > right) return -1; // 如果左边大于右边&#xff0c;那么…

esp8266与uno使用软串口通信

esp8266的d6和d5分别与uno的5和6管脚连接&#xff1a; uno程序&#xff1a; //uno #include <SoftwareSerial.h> SoftwareSerial s(5,6);//(RX,TX)void setup(){s.begin(9600);Serial.begin(9600); }void loop(){int data50;if (s.available() > 0) {char c s.read(…

【错题集-编程题】比那名居的桃子(滑动窗口 / 前缀和)

牛客对应题目链接&#xff1a;比那名居的桃子 (nowcoder.com) 一、分析题目 1、滑动窗口 由题意得&#xff0c;我们是要枚举所有大小为 k 的子数组&#xff0c;并且求出这段⼦数组中快乐值和羞耻度之和。因此&#xff0c;可以利用滑动窗口的思想&#xff0c;用两个变量维护大小…

【区块链】共识算法简介

共识算法简介 区块链三要素&#xff1a; 去中心化共识算法智能合约 共识算法作为区块链三大核心技术之一&#xff0c;其重要性不言而喻。今天就来简单介绍共识算法的基本知识。 最简单的解释&#xff0c;共识算法就是要让所有节点达成共识&#xff0c;保证少数服从多数&#x…

从零开始学AI绘画,万字Stable Diffusion终极教程(六)

【第6期】知识补充 欢迎来到SD的终极教程&#xff0c;这是我们的第六节课&#xff0c;也是最后一节课 这套课程分为六节课&#xff0c;会系统性的介绍sd的全部功能&#xff0c;让你打下坚实牢靠的基础 1.SD入门 2.关键词 3.Lora模型 4.图生图 5.controlnet 6.知识补充 …

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

C/C++开发,opencv-ml库学习,K近邻(KNN)应用

目录 一、k近邻算法 1.1 算法简介 1.2 opencv-k近邻算法 二、cv::ml::KNearest应用 2.1 数据集样本准备 2.2 KNearest应用 2.3 程序编译 2.4 main.cpp全代码 一、k近邻算法 1.1 算法简介 K近邻算法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;基本原理是…

Vue按照顺序实现多级弹窗(附Demo)

目录 前言1. 单个弹窗2. 多级弹窗 前言 强化各个知识点&#xff0c;以实战融合&#xff0c;以下两个Demo从实战提取 1. 单个弹窗 部署按钮框以及确定的方法即可 截图如下所示&#xff1a; 以下Demo整体逻辑如下&#xff1a; 点击“生成周月计划”按钮会触发showWeekPlanDia…

FLIR LEPTON3.5 热像仪wifi 科研实验测温采集仪

点击查看详情!点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情 1、描述 这是一款桌面科研实验测温热成像多功能热像记录仪&#xff0c;小巧轻便…

STM32微秒级别延时--F407--TIM1

基本配置&#xff1a; TIM1挂载在APB2总线上&#xff0c;150MHz经过15分频&#xff0c;得到10MHz计数频率&#xff0c;由于disable了自动重装载&#xff0c;所以只需要看下一次计数值是多少即可。 void TIM1_Delay_us(uint16_t us) //使用阻塞方式进行延时&#xff0c;ARR值不…

记录vue报错问题 in ./node_modules/axios/lib/platform/index.js

今天这个问题困扰了我许久 报错内容如下&#xff1a; 最初一直以为是我没装axios&#xff0c;又重新装了一次&#xff0c;后面才发现是axios版本原因&#xff0c;真的总是被版本的原因困住真的很烦 解决方法如下&#xff1a; 将axios的版本改为1.5.0 1、打开项目的文件夹“…