接上一回C++:补继承漏洞+多态原理(带图详解)

news2024/9/17 9:14:00

引子:接上一回我们讲了继承的分类与六大默认函数,其实继承中的菱形继承是有一个大坑的,我们也要进入多态的学习了。

注意:我学会了,但是讲述上可能有一些不足,希望大家多多包涵

继承复习:

1,继承中的友元函数不能继承!(就比如说,你爸爸的朋友不是你朋友,你朋友不时你爸爸的朋友)

2,继承中的static:基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子 类,都只有一个static成员实例(可以通过地址来看是否一样)

继承补漏洞(数据冗余与二义性):

1. 什么是菱形继承?菱形继承的问题是什么?

菱形继承:菱形继承是多继承的一种特殊情况。

菱形继承有数据冗余和二义性的问题

如下图:西红柿,里面就有二个植物的属性,这与我们常识不符

2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的

关键字:virtual ,虚拟继承可以解决菱形继承的二义性和数据冗余的问题

3. 继承和组合的区别?什么时候用继承?什么时候用组合?

继承其实是is a的关系,组合其实是has a 的关系

组合耦合度更低,关联性较低,比较适用与接口,继承耦合度更高,关联性比较强,比较收到父类的较大影响,往往一发而牵全身,故在现实生活,公司里能用组合就用组合,在继承的情况下就使用继承,(其实也是一种"黑箱白盒"的思想),白盒要求更高,黑盒则只要功能实现,故黑盒难度更低,而我们组合本质上就是一种黑盒,实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有 些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用 继承,可以用组合,就用组合。

多态基础知识:

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。

多态的构成条件

1. 必须通过基类的指针或者引用调用虚函数

2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

虚函数:

即被virtual修饰的类成员函数称为虚函数,虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的 返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数

抽象类:

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生 类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承

虚函数重写的两个例外:

1. 协变(基类与派生类虚函数返回值类型不同) 派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指 针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

2. 析构函数的重写(基类与派生类析构函数的名字不同) 如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字, 都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同, 看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处 理,编译后析构函数的名称统一处理成destructor。

试例代码:

class person
{
public:
    //virtual void ticket() = 0
    //在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口
    //类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生
        //类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承
    virtual void ticket()
    {
        cout << "买全票" << endl;
    }
    virtual ~person()
    {
        cout << "~person" << endl;
    }
};
class children :public person
{
public:
    virtual void ticket()
    {
        cout << "买半票" << endl;
    }
    ~children()
    {
        cout << "~children" << endl;
    }
};
class soldier :public person
{
public:
    virtual void ticket()
    {
        cout << "优先买票" << endl;
    }
    ~soldier()
    {
        cout << "~soldier" << endl;
    }
};
//产生多态的条件(语法规定)
//1,虚函数重写
//2,必须是父类引用调用虚函数或者父类指针

void func1(person &p)
{
    p.ticket();
}
void func2(person*p)
{
    p->ticket();
}


//协变
//析构函数的重写,编译器在编译的时候,统一变为destructor;
int main()
{
    person s1;
    children s2;
    soldier s3;
    func1(s1);
    func1(s2);
    func2(&s1);
    func2(&s2);
    func1(s3);
    func2(&s3);
    //隐藏关系大于重写
    s2.ticket();
    person* s4 = new soldier;
    person* s5 = new person;
    person* s6 = new children;
    delete s5;
    delete s4;
    delete s6;

    return 0;

}

多态的原理:

我们先看一下以下代码的结果:

//查看虚拟函数的地址-->虚表
//虚函数开辟有一定的空间消耗
    //参看各个虚函数的地址,与普通函数的区别
class Base
{
public:
    virtual void Func1()
    {
        cout << "Base::Func1()" << endl;
    }
    virtual void Func2()
    {
        cout << "Base::Func2()" << endl;
    }
    void Func3()
    {
        cout << "Base::Func3()" << endl;
    }
private:
    int _b = 1;
};
class Derive : public Base
{
public:
    virtual void Func1()
    {
        cout << "Derive::Func1()" << endl;
    }
private:
    int _d = 2;
};
void func(Base& p)
{
    //这是动态的一个(直接在虚表中找到的func)
    p.Func1();
    //静态编译的func(是在链接产生的符号表!)
    p.Func3();
}
//切割父类的那一部分!
int main()
{
    Base b;
    Base b2;
    Base b3;
    Derive d;
    func(b);
    func(d);
    int h1=2;
    static int h2=3;
    int* h3 = new int;
    const int h4=1;
    printf("栈区%p\n", &h1);
    printf("静态区:%p\n", &h2);
    printf("堆区:%p\n", &h3);
    printf("常量区:%p\n", &h4);
    //相关关系关联的成度
    printf("%p\n", *((int*)&b));
    printf("%p\n", *((int*)&d));
    /*printf("%p   ", &Base::Func1);
    printf("%p   ", &Base::Func2);
    printf("%p   ", &Base::Func3);
    printf("%p   ", &Derive::Func1);*/
    return 0;
}


结果运行如下:

一:虚函数指针存在对象中,有一个void**的指针

二:虚函数的地址相对其他非常相近,都是在代码区,

三,(1),多个虚函数地址如果类型一样,他们的地址都是一样的,就是共享虚表(2),注意取出前四位地址是*((int*)&b);(3),虚函数表,存在常量区

四,继承的虚函数地址没有改变,重写的虚函数地址改变,

多态原理文字解释(虚表本质是函数指针数组):

void->虚函数,存在代码区

void*->虚函数表,存在常量区

void**->虚函数表的指针,存在对象中

看出满足多态以后的函数调用,不是在编译时确定的,是运行 起来以后到对象的中取找的。不满足多态的函数调用时编译时确认好的。

其实多态本质上是一种动态绑定又称后期绑定。普通函数是静态绑定又称为前期绑定(早绑定),

什么是动态绑定与静态绑定?如下

1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态, 比如:函数重载 2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体 行为,调用具体的函数,也称为动态多态。 3. 本小节之前(5.2小节)买票的汇编代码很好的解释了什么是静态(编译器)绑定和动态(运行时)绑定。

二道面试题:

多继承中指针偏移问题?下面说法正确的是(C )

class Base1 {  public:  int _b1; };

class Base2 {  public:  int _b2; };

class Derive : public Base1, public Base2 { public: int _d; };

int main(){ Derive d; Base1* p1 = &d; Base2* p2 = &d; Derive* p3 = &d; return 0; }

A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

下面哪种面向对象的方法可以让你变得富有( ) '

A: 继承 B: 封装 C: 多态 D: 抽象

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

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

相关文章

绿洲生态OAS:探索数字新绿洲,共创价值新纪元

绿洲生态OAS的盛大启航 在数字经济的浩瀚星海中&#xff0c;一颗名为绿洲生态OAS的璀璨明珠即将于7月13日全面正式上线。这是一场对未来的探索&#xff0c;一次对财富自由的勇敢追求。绿洲生态&#xff0c;简称OAS&#xff0c;以其独特的积分发行规划&#xff0c;吸引了无数投…

电脑工具箱神器——uTools

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 很多人脑子里都有一些一个月只用两三次的软件&#xff0c;这些软件就这样积满了灰尘&#xff0c;需要的时候又不知道去哪里找。uTools 完美地解决…

C语言补充:指针的基础理解

1.int* 和 char* 的修改字节上的区别及指针的运算 先看两段代码&#xff1a; int a 0x11223344; int* pa &a; *pa 0;int a 0x11223344; char* pa &a; *pa 0;这里我们不难发现对于指针的改变其实是取决于对应的指针类型的&#xff0c;32位环境下char就是一个字节…

干货:如何高效检索和阅读文献

前言:Hello大家好,我是小哥谈。高效检索和阅读文献是科研过程中非常重要的一环,它能够帮助我们快速找到所需的信息并进行深入的学习和理解。本文就说明一下如何高效检索和阅读文献。🌈 目录 🚀1.炼成“高搜商” 🍀🍀1.1 文献检索 🍀🍀1.2 ⽂献树思维 �…

c++初阶知识——类和对象(1)

目录 1.类和对象 1.1 类的定义 1.2 访问限定符 1.3 类域 2.实例化 2.1 实例化概念 2.2 对象大小 内存对齐规则 3.this指针 1.类和对象 1.1 类的定义 &#xff08;1&#xff09;class为定义类的关键字&#xff0c;Stack为类的名字&#xff0c;{}中为类的主体&#xf…

数据(图像)增广

一、数据增强 1、增加一个已有数据集&#xff0c;使得有更多的多样性&#xff0c;比如加入不同的背景噪音、改变图片的颜色和形状。 2、增强数据是在线生成的 3、增强类型&#xff1a; &#xff08;1&#xff09;翻转 &#xff08;2&#xff09;切割 &#xff08;3&#xf…

AI大模型API:开启智能应用的新纪元

AI大模型API是当今技术领域的重要突破&#xff0c;它们以其卓越的性能和强大的计算能力引领着人工智能的发展。这些API不仅仅是一种技术工具&#xff0c;更是推动智能化时代的核心驱动力。通过AI大模型类API&#xff0c;我们可以利用先进的算法和深度学习模型&#xff0c;实现各…

k8s NetworkPolicy

Namespace 隔离 默认情况下&#xff0c;所有 Pod 之间是全通的。每个 Namespace 可以配置独立的网络策略&#xff0c;来 隔离 Pod 之间的流量。 v1.7 版本通过创建匹配所有 Pod 的 Network Policy 来作为默认的网络策略 默认拒绝所有 Pod 之间 Ingress 通信 apiVersion: …

heic格式转化jpg?三种方法轻松解决Heic图片转换

HEIC是苹果IOS 11系统推出的图片格式&#xff0c;它采用了最新的图片处理技术&#xff0c;在不损坏图片质量的情况下&#xff0c;减少占用系统。很多使用mac系统的小伙伴都有这样的困惑&#xff0c;如何将HEIC格式的图片转换成JPG呢&#xff1f;小编今天给大家分享3种适用的hei…

Oracle安装完之后设置开机自启动配置步骤

默认&#xff1a;dbca建库时都会自动创建/etc/oratab文件 [oraclelocalhost bin]# vi /etc/oratab 将“orcl:/home/oracle/product/11.2.0/dbhome_1:N”&#xff0c;改为 “orcl:/home/oracle/product/11.2.0/dbhome_1:Y”。 修改完成后&#xff0c;保存退出--选项代表开机是…

不仅是输出信息,console.log 也能玩出花

console.log 是 JavaScript 中一个常用的函数&#xff0c;用于向控制台输出信息。 console.log 虽然主要用于调试目的&#xff0c;但也包含了一些有趣的用法&#xff0c; console.log 不仅能输出文本&#xff0c;还能以更丰富的方式展示信息。 比如我们打开 B 站&#xff0c;然…

Win10安装MongoDB(详细版)

文章目录 1、安装MongoDB Server1.1. 下载1.2. 安装 2、手动安装MongoDB Compass(GUI可视工具)2.1. 下载2.2.安装 3、测试连接3.1.MongoDB Compass 连接3.2.使用Navicat连接 1、安装MongoDB Server 1.1. 下载 官网下载地址 https://www.mongodb.com/try/download/community …

ElasticSearch(二)【基本操作以及集成 SpringBoot】

启动 elasticsearch&#xff08;脚本&#xff09;、kibana&#xff08;bat脚本&#xff09; 和 elasticsearch-head-master&#xff08;npm run start&#xff09; 1、RESTful 风格 1.1、索引类基本操作 1.1.1、创建索引 PUT /索引名/类型名/文档id {请求体} 在 elasticsear…

Rewrk一个更现代的http框架基准测试实用程序

Rewrk一个更现代的http框架基准测试实用程序。HTTP基准测试&#xff08;HTTP benchmarking&#xff09;是一种测量和评估HTTP服务器或应用程序性能指标的活动。其目的是在特定条件下模拟大量用户请求&#xff0c;以测量服务器或应用程序的响应能力、吞吐量、延迟等指标&#xf…

员工微信号怎么管理

员工走私单或者离职了删除客户&#xff0c;删除撤回的消息也能看得见

强引用?软引用?弱引用?虚引用?一文带你彻底搞懂!!

强引用&#xff1f;软引用&#xff1f;弱引用&#xff1f;虚引用&#xff1f;一文带你彻底搞懂&#xff01;&#xff01; 前言引用级别强引用软引用弱引用虚引用 总结 前言 我们知道 JVM 的 GC 是不受程序控制的&#xff0c;只要满足条件就会自动触发 那他是根据什么触发的呢…

Apache防盗链、网页压缩、网页缓存

目录 网页压缩 类型 示例 动态添加模块操作步骤 重装Apache操作步骤 网页缓存 示例 操作步骤 隐藏版本信息 操作步骤 Apache防盗链 定义 原理 配置防盗链实验环境 实验环境 本地图片盗链示例 操作步骤 防盗链示例 操作步骤 网页压缩 网站的访问速度是由多个…

C语言 结构体和共用体——对结构体的操作

目录 如何访问结构体的成员&#xff1f; 结构体变量的赋值操作 结构体变量的取地址值操作 如何访问结构体的成员&#xff1f; 结构体变量的赋值操作 结构体变量的取地址值操作

C++基础技能:如何在VisualStudio中使用clang-format格式化代码

目录 1.下载 clang-format.exe程序 2.clang-format详细参数说明 3.Visual Studio中设置使用clang-format 4.创建.clang-format文件 5.在Visual Studio中触发格式化 1.下载 clang-format.exe程序 下载源&#xff1a; https://llvm.org/builds/下载最新的clang-format。ht…

开发编码规范笔记

前言 &#xff08;1&#xff09;该博客仅用于个人笔记 格式转换 &#xff08;1&#xff09;查看是 LF 行尾还是CRLF 行尾。 # 单个文件&#xff0c;\n 表示 LF 行尾。\r\n 表示 CRLF 行尾。 hexdump -c <yourfile> # 单个文件&#xff0c;$ 表示 LF 行尾。^M$ 表示 CRLF …