新C++(7):多态那些事儿_上

news2024/10/7 18:20:46

"也应该歌颂赞美那株鲜红的玫瑰。"

一、回顾多态

(1)什么是多态呢

在编程语言和类型轮中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。 取自这里

所谓多态,通俗来讲就是多种形态。当不同的对象去执行同一个行为,产生处不同的结果和状态。我们举一个买车票的例子,普通人买车票 对应的是"全价",学生呢买车票则对应的是"半价",军人买票呢,对应的是"优先买票"。

(2)多态的定义

多态形成的条件:
①虚函数完成重写
②父类的指针或者引用去调用虚函数

虚函数;

什么是虚函数呢?C++定义了一个关键字virtual,这个肯定不陌生,因为在前篇谈论虚继承的时候,这位仁兄出现过,展现出了它惊人的一面。

virtual void func(){}

虚函数的重写(覆盖)要求:
派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同)

我们根据上述的场景,简单来实现一份多态的代码吧~

class Person
{
public:
    virtual void BuyTicket() { cout << "Person--买票-全价" << endl; }
};

class Student :public Person
{
    virtual void BuyTicket() { cout << "Student -- 买票-半价" << endl; }
};

class Soidler :public Person
{
    virtual void BuyTicket() { cout << "Soidler -- 买票-军人优先" << endl; }
};

此时还不是多态。因为我们都知道,那个对象就调用哪个对象的类成员方法。我们还差一个条件,"父类的指针引用去调用虚函数"。

显然,颠覆我们的认知。不管是student、soidler、person都传递给父类引用,但是因为多态的条件达成,那么传递的什么类型,就去调用什么类型中被重写(覆盖)。

(3)override\final

继承final:

如果你不想让一个类被继承,那么你应该让它的构造函数声明为私有;

这是C++11之期解决类不能被继承的写法。C++11后引入了final关键字表示该类不能被继承。

虚函数final:

final:修饰虚函数,表示该虚函数不能再被重写。

overrid

检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

二、多态与析构函数

在继承中,我们层提到,在子类的析构函数中,不用显式去调用父类的析构函数,因为当子类的析构函数结束后,就会去调用父类的析构函数。

我们来看看下面的一段代码;

class Person
{
public:
     ~Person()
    {
        cout << "Person delete:" << _p << endl;
        delete[] _p;
    }
protected:
    int* _p = new int[10];
};

class Student : public Person
{
public:
    ~Student()
    {
        cout << "Student delete:" << _s << endl;
        delete[] _s;
    }
protected:
    int* _s = new int[20];
};


int main()
{
    Person* ptr1 = new Person;
    Person* ptr2 = new Student;
    
    delete ptr1;
    delete ptr2;

    return 0;
}

delete类,要做两件事:调用释放类的析构函数,再调用operator.delete()。

我们析构ptr1那么很好,它成功地帮我释放了Person中的数组。delete ptr2时,它仅仅帮我们完成了父类的析构函数。难道你会指着编译器抱怨,不去释放那子类对象动态开辟的空间,而造成的内存泄漏的问题?当然不行!这怨不得人家,因为它严格按照,自己是什么类,就去调用该类的拥有的什么成员方法。

似乎我们预想的是,让ptr2delete的时候,不是去调用它类型的析构,而是去调用new的对Student的析构。看到这里我们想到了什么??? 父类的指针引用调用重写的虚函数构成多态!

 class Person
{
public:
    //虚函数
    virtual ~Person()
    {
        cout << "Person delete:" << _p << endl;
        delete[] _p;
    }
protected:
    int* _p = new int[10];
};

class Student : public Person
{
public:
    //虚函数
    virtual ~Student()
    {
        cout << "Student delete:" << _s << endl;
        delete[] _s;
    }
protected:
    int* _s = new int[20];
};

同时,这里也回答了上一篇中,为什么在编译器看来基类与派生类的析构函数,都会做特殊处理成一样的函数(destructor)。就是为了 实现多态的析构函数。

请记住:
"为多态基类声明virtual析构函数"。这是永远不会错的。

三、sizeof(Base)

我们看看下面的代码,你能否计算该类的大小?

class A
{
public:
    void Func(){}
private:
    int _a;
    char _b;
};

内存对齐!这可难不到我,_a占4字节,_b占1字节,内存对齐到8。A的大小为8byte。

嗯?还不错,确实是8字节。

class A
{
public:
    virtual void Func(){}
private:
    int _a;
    char _b;
};

这难道不是同一道题?这么送分的嘛?

这怎么回事儿?为什么加上虚函数,类的大小就变了呢?

这个是因为,当类有虚函数时,就会给类增加一个结构,"虚函数表指针"。那么这个虚函数表指针的讲解不在本篇,也就此略过。

四、抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象

同时,这也要求了,该基类的派生类必须完成重写,否则无任何意义。

class Car
{
public:
     //纯虚函数
    virtual void Drive() = 0;
};

class BMW : public Car
{
public:
};

int main()
{
    BMW b;
    return 0;
}
class Car
{
public:
     //纯虚函数
    virtual void Drive() = 0;
};

class BMW : public Car
{
public:
    virtual void Drive()
    {
        cout << "驾驶乐趣" << endl;
    }
};

int main()
{
    BMW b;
    return 0;
}

接口继承vs实现继承:

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。

虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。

五、经典考察

我们最后以一道面试题作为本篇的结束。

class A
{
public:
    virtual void func(int val = 1){ std::cout << "A->" << val << std::endl; }
    void test() { func(); }
};

class B : public A
{
public:
    virtual void func(int val = 0){ std::cout << "B->" << val << std::endl; }
};

int main(int argc, char* argv[])
{
    B*p = new B;
    p->test();
    return 0;
}

程序应该打印成什么?

最后也就是这个结果。

总结:

①final+类 该类不可被继承。final+函数该函数不可被重写。 overrid编译时检查子类虚函数重写。

②记住,在多态基类最好实现成虚函数。

③包含纯虚函数的类是抽象类,纯虚函数就是在虚函数 =0。

本篇也就在这里告一段落,感谢你的阅读。

祝你好运~向阳而生。

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

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

相关文章

FreeRTOS任务通知实验

从 V8.2.0 版本开始&#xff0c;FreeRTOS 新增了任务通知这个功能&#xff0c;可以使用任务通 知来代替信号量、消息队列、事件组等这些东西。使用任务通知的话效率会更高。 本章要实现的功能是&#xff1a;使用任务通知方式实现消息队列、二值信号量、计数信号 量、事件标记功…

极狐场景化造车理念受热捧,北汽蓝谷构建未来5年核心竞争力

近日&#xff0c;极狐汽车以“一米视角”为设计思考的原点&#xff0c;围绕亲子出行的全场景&#xff0c;推出全球首款智能亲子车——考拉。作为北汽蓝谷场景化造车的首款产品&#xff0c;极狐汽车考拉无疑是继高阶智能驾驶标杆产品HI之后的又一次先行探索&#xff0c;致力于卡…

1月VR大数据:Quest 2增长2.91%,HTC份额跌至10%以下

Hello大家好&#xff0c;每月一期的VR内容/硬件大数据统计又和大家见面了。 想了解VR软硬件行情么&#xff1f;关注这里就对了。我们会统计Steam平台的用户及内容等数据&#xff0c;每月初准时为你推送&#xff0c;不要错过喔&#xff01;本数据报告包含&#xff1a;Steam VR硬…

初识C语言(对c语言的简单介绍)

初识C语言什么是C语言&#xff1f;第一个C语言程序数据类型类型的使用&#xff1a;变量、常量定义变量的方法变量的分类变量的使用变量的作用域和生命周期常量字符串转义字符注释字符串转义字符注释选择语句循环语句函数数组数组定义数组的使用操作符常见关键字关键字 typedef关…

MySQL进阶篇之SQL优化

03、SQL优化 3.1、插入数据 1、insert优化 批量插入 INSERT INTO 表名 (字段1,字段2,...) VALUES (值1,值2,...),(值1,值2,...),(值1,值2,...);INSERT INTO 表名 VALUES (值1,值2,...),(值1,值2,...),(值1,值2,...);手动提交事务 start transaction; INSERT INTO 表名 (字段1…

【SQL 审核查询平台】Archery使用介绍

Archery 读作&#xff1a;[ˈɑːrtʃəri] Archery目录界面截图功能清单依赖清单框架前端组件服务端部署准备运行配置启动访问修改配置项基础设置添加实例添加资源组资源组关联用户/实例添加权限组用户关联权限组/权限设置工单上线和查询的审批流程设置默认资源组和默认权限组…

数组的几种常见方法及其返回值

push()&#xff1a;向数组的末尾添加一个或多个元素&#xff1b;返回的是数组的新长度。unshift()&#xff1a;向数组的开头添加一个或多个元素&#xff1b;返回的是数组的新长度。shift()&#xff1a;删除数组的第一个元素&#xff0c;并返回被删除的&#xff08;即第一个元素…

品牌社交营销链路 | 小红书数据分析网站

【导语】 2022年&#xff0c;小红书品牌推广竞争愈演愈烈&#xff0c;从小红书用户画像分析&#xff0c;到抢占小红书关键词排名&#xff0c;营销动作内卷升级&#xff0c;那么在2023的新篇章&#xff0c;如何打通社交种草的链路呢&#xff1f; 1、运营企业账号&#xff0c;建立…

MQTT 代理助力ECARX实现汽车智能互联

一、应用背景 ECARX是中国汽车制造商吉利旗下的一家科技创新企业&#xff0c;致力于持续打造行业领先的智能网联生态开放平台&#xff0c;全面为车企赋能&#xff0c;创造更智能、更安全的出行体验&#xff0c;为智能互联汽车提供智能解决方案。 ECARX主要业务包括吉利汽车的…

让Apache Beam在GCP Cloud Dataflow上跑起来

简介 在文章《Apache Beam入门及Java SDK开发初体验》中大概讲了Apapche Beam的简单概念和本地运行&#xff0c;本文将讲解如何把代码运行在GCP Cloud Dataflow上。 本地运行 通过maven命令来创建项目&#xff1a; mvn archetype:generate \-DarchetypeGroupIdorg.apache.b…

Swift 新 async/await 同步机制小技巧:消除“多余”的 await 关键字

概览 在使用多个Actor 共同实现同步功能的时候&#xff0c;我们往往会看到如下使用场景&#xff1a; Actor A 必须在主线程上运行&#xff0c;Actor B可以在任意线程上运行&#xff0c;但需要适时的调用 Actor A 中的方法。 在这种情况下&#xff0c;我们会遇到如下代码&#…

从移动激光扫描数据中自动提取单棵树的双重生长方法

论文题目&#xff1a;A dual growing method for the automatic extraction of individual trees from mobile laser scanning data Abstract 在城市场景的杂乱点云中&#xff0c;街道树木与其他物体交织在一起&#xff0c;阻碍了对单个树木的自动提取。根据树木的一般构成&a…

React:安装配置使用scss

目录 前言&#xff1a; 1.暴露隐藏的webpack配置&#xff1b; 2.安装sass的相关包&#xff1b; 3.项目中新建一些scss文件&#xff1b; 4.在config文件夹中找到webpack.config.js文件&#xff0c;进行配置&#xff1b; 5.测试使用&#xff1b; 前言&#xff1a; 项目采用…

Python之Pandas的常用技能【写入数据】

1、背景&#xff1a; 最近在工作中遇到越来越多的的使用pandas或者python来处里写入操作&#xff0c;尤其是对excel文件或者csv文件的操作更是常见&#xff0c;这里将写入操作总结如下&#xff0c;方便记忆&#xff0c;也分享给大家&#xff0c;希望对阅读者能够有所帮助 2、…

nvdiffrec:Extracting Triangular 3D Models, Materials, and Lighting From Images

论文主页 https://nvlabs.github.io/nvdiffrec/git主页 https://github.com/NVlabs/nvdiffrec新闻报道 https://redian.news/wxnews/36324YuQiao0303 读后感 https://blog.csdn.net/qq_34342853/article/details/125622816b站演示效果视频 https://www.bilibili.com/video/BV1P…

8天获offer|祝贺信息技术老师获CSC资助赴意大利访学

I老师拟申报CSC青年骨干教师项目&#xff0c;指定欧洲学校&#xff0c;且要求半个月内获得邀请函。我们8天就取得了意大利帕多瓦大学的offer&#xff0c;研究方向完全相符&#xff0c;因而顺利通过了CSC审批。后经繁琐的手续&#xff0c;I老师最终获得签证&#xff0c;如期出国…

ABB机器人设置有效载荷的2种方法具体步骤(直接输入法+自动识别推算法2)

ABB机器人设置有效载荷的2种方法具体步骤(直接输入法+自动识别推算法2) 为什么要设置有效载荷Loaddata? 对于搬运应用的机器人只有设定正确的工具和载荷数据,机器人才能正确的工作; 对于搬运比较重的产品,或工具的重量也比较重,需要设置工具及搬运对象的重心和重量; …

2023年黑马Java入门到精通教程--程序流程控制

程序流程控制 程序执行的几种常见形式 分支结构 If分支 根据判定的结果&#xff08;真或假&#xff09;决定执行某个分支的代码 If分支的作用 If分支有三种格式 switch分支 也是匹配条件去执行分支, 适合做值匹配的分支选择&#xff0c;结构清晰&#xff0c;格式良好 swit…

通信原理笔记—基带信号的检测与最佳接收

目录 在加性白高斯噪声信道条件下数字基带信号的接收&#xff1a; 加性高斯白噪声干扰下的信号检测&#xff1a; 最大似然判决准则&#xff1a;误码率最小意义上的最佳判决&#xff1a; 先验等概及最佳判决时的误码率计算&#xff1a; 高斯噪声干扰下二进制信号的检测&…

Mock.js(简单代替后台)

Mock.js &#xff08;官网http://mockjs.com/&#xff09;是一款模拟数据生成器&#xff0c;旨在帮助前端开发人员独立于后端进行开发&#xff0c;帮助编写单元测试。二、为什么使用mockjs在做开发时&#xff0c;当后端的接口还未完成&#xff0c;前端为了不影响工作效率&#…