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

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

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

继承复习:

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.mfbz.cn/a/792989.html

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

相关文章

并查集+链表,CF 1131F - Asya And Kittens

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1131F - Asya And Kittens 二、解题报告 1、思路分析 本质是拼积木游戏 初始有n块积木&#xff0c;每次两块首尾拼成一块就行&#xff0c;拼接n - 1 次最后会得到一个大积木&#xff0c;我们从左往右输出组…

Nuxt3封装网络请求 useFetch $fetch

前言&#xff1a; 刚接触、搭建Nuxt3项目的过程还是有点懵的&#xff0c;有种摸石头过河的感觉&#xff0c;对于网络请求这块&#xff0c;与之前的Vue3项目有所区别&#xff0c;在Vue项目通常使用axios这个库进行网络请求&#xff0c;但在Nuxt项目并不推荐&#xff0c;因为有内…

【PostgreSQL】Spring boot + Mybatis-plus + PostgreSQL 处理json类型情况

Spring boot Mybatis-plus PostgreSQL 处理json类型情况 一、前言二、技术栈三、背景分析四、方案分析4.1 在PostgreSQL 数据库中直接存储 json 对象4.2 在PostgreSQL 数据库中存储 json 字符串 五、自定义类型处理器5.1 定义类型处理器5.2 使用自定义类型处理器 一、前言 在…

【PowerShell】-1-快速熟悉并使用PowerShell

目录 PowerShell是什么&#xff1f;和CMD的区别&#xff1f; PowerShell的演变 自动化IT管理任务 一些名词 详尽的PowerShell开始之路 1.打开PowerShell&#xff1a; 2.基本命令&#xff1a; &#xff08;1&#xff09;Get-Process &#xff08;2&#xff09;变量赋值…

React Hooks学习笔记

一、usestate的使用方法-初始化state函数 import React, { useState } from "react"; function App() {const [count, setCount] useState(0);return (<div><p>点击{count}次</p><button onClick{() > setCount(count 1)}>点击</bu…

【Linux系统】信号量(初次理解)

五个概念 多个执行流&#xff08;进程&#xff09;&#xff0c;能看到的一份资源&#xff1a;共享资源被保护起来的资源可以叫临界资源&#xff08;同步和互斥&#xff09; --- 用互斥的方式保护共享资源就叫临界资源互斥&#xff1a;任何时刻只能有一个进程在访问共享资源资源…

就业平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;企业管理&#xff0c;企业类型管理&#xff0c;留言板管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;招聘信息&#xff0c;简历&#xff0c;我的 …

SpringCloud--Eureka集群

Eureka注册中心集群 为什么要集群 如果只有一个注册中心服务器&#xff0c;会存在单点故障&#xff0c;不可以高并发处理所以要集群。 如何集群 准备三个EurekaServer 相互注册&#xff0c;也就是说每个EurekaServer都需要向所有的EureakServer注册&#xff0c;包括自己 &a…

游戏如何应对黑灰产工作室

游戏黑灰产工作室&#xff0c;是指以非法渠道、非法手段通过游戏进行牟利的团伙。使用脚本、外挂是黑灰产工作室的显著特征&#xff0c;其常见的牟利方式有&#xff1a;打金工作室、资源囤积号、初始号、自抽号、代练工作室以及营销欺诈等。 ▲ 常见的游戏黑灰产工作室牟利路径…

PMP证书 怎么报名?

首先&#xff0c;PMP证书相当于某些行业的敲门砖&#xff0c;身处项目管理相关行业的人应该清楚&#xff0c;这个证书&#xff0c;可能是你升职的不可或缺的一把钥匙。首先&#xff0c;我们先来了解一下什么是pmp。 1充分了解PMP PMP&#xff08;项目管理专业人士资格认证&am…

idm站点抓取可以用来做什么 idm站点抓取能抓取本地网页吗 idm站点抓取怎么用 网络下载加速器

在下载工具众多且竞争激烈的市场中&#xff0c;Internet Download Manager&#xff08;简称IDM&#xff09;作为一款专业的下载加速软件&#xff0c;仍然能够赢得众多用户的青睐&#xff0c;这都要得益于它的强大的下载功能。我们在开始使用IDM的时候总是有很多疑问&#xff0c…

「iOS」暑假第一周 —— ZARA的仿写

暑假第一周 ZARA的仿写 文章目录 暑假第一周 ZARA的仿写写在前面viewDidLoad 之中的优先级添加自定义字体下载想要的字体添加至info之中找到字体名字并应用 添加应用图标和启动页面 写在前面 暑假第一周留校学习&#xff0c;对于ZARA进行了仿写&#xff0c;在仿写的过程之中&a…

探索创意无限:独特的平面设计趋势与案例分享

随着平面设计领域的不断发展&#xff0c;平面设计趋势也在不断变化。在一个信息爆炸的时代&#xff0c;设计不仅仅是视觉的表达&#xff0c;更是思想和情感的交流。到了 2024 年&#xff0c;一些新的平面设计趋势已经开始显现&#xff0c;同时一些旧的趋势得到了新的发展和再度…

Jenkins安装部署与配置

目录 前言 Jenkins 的主要功能 Jenkins 的工作流程 一. 环境准备 二. 安装JDK 三. 安装Tomcat 四. 部署Jenkins 五. 浏览器访问 六. 修改超级管理员默认密码 七. 系统配置 八. 安装插件 九. 手动部署插件 前言 Jenkins 是一个开源的自动化服务器&#xff0c;用于…

C# 串口数据转网口实现空气风速风向检测

1.窗体搭建 添加time(定时器) 因为需要风速和风向自动刷新 2.进行网口空气检测 ①服务器连接按钮 // 连接按钮private void button1_Click(object sender, EventArgs e){if (button1.Text "连接"){ConnectSocke();// 连接服务器}else{CloseSocket(); // 关闭服务器…

苹果提出RLAIF:轻量级语言模型编写代码

获取本文论文原文PDF&#xff0c;请在公众号【AI论文解读】留言&#xff1a;论文解读 代码生成一直是一个充满挑战的领域。随着大型语言模型&#xff08;LLM&#xff09;的出现&#xff0c;我们见证了在自然语言理解和生成方面的显著进步。然而&#xff0c;当涉及到代码生成&a…

XD文件打开神器:这个在线工具你一定要试试!

你有没有遇到过对设计师发来的XD文件没有头绪&#xff1f;不知道XD文件深层含义&#xff1f;如何打开XD文件最省时省力&#xff1f;这篇文章告诉你答案。 https://ad.js.design/online/xd/?sourcecsdn&planxy711 XD文件是什么&#xff1f; 事实上&#xff0c;XD文件就是…

C++入门基础篇(1)

欢迎大家来到海盗猫鸥的博客—— 断更许久&#xff0c;让我们继续好好学习吧&#xff01; 目录 1.namespace命名空间 命名空间的存在价值&#xff1a; 命名空间的定义&#xff1a; 命名空间的使用&#xff1a; 2.C输入输出函数 使用&#xff1a; 3.缺省参数 4.函数重载…

界面组件Kendo UI for React 2024 Q2亮点 - 生成式AI集成、设计系统增强

随着最新的2024年第二季度发布&#xff0c;Kendo UI for React为应用程序开发设定了标准&#xff0c;包括生成式AI集成、增强的设计系统功能和可访问的数据可视化。新的2024年第二季度版本为应用程序界面提供了人工智能(AI)提示&#xff0c;从设计到代码的生产力增强、可访问性…

linux-5.10.110内核源码分析 - Freescale ls1012a pcie msi中断

1、dts msi控制器描述 1.1、dts描述 msi: msi-controller11572000 {compatible "fsl,ls1012a-msi";reg <0x0 0x1572000 0x0 0x8>;msi-controller;interrupts <0 126 IRQ_TYPE_LEVEL_HIGH>; };ls1012a msi控制器具体介绍可以参考官网手册”25.1.1 PC…