C++多态 学习笔记(上)

news2024/9/25 11:14:32
    本文涉及的指针都是 4bytes
    如果要其他平台下,部分代码需要改动。比如:如果是 x64 程序,则需要考虑指针是 8bytes 问题 等等。

什么是多态?

举个例子:比如 买票这个行为 ,当 普通人 买票时,是全价买票; 学生 买票时,是半价买票; 军人
买票时是优先买票。同样的行为被不同的对象执行,效果不一样。再比如,对于不同的动物我们封装成类,有一个接口是 叫() , 不同的动物调用这个接口,就会有不同的效果。

多态是基于继承的概念:

在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如 Student 继承了
Person Person 对象买票全价, Student 对象买票半价。

 

介绍多态之前,先学习几个概念:虚函数 重写

 


1.虚函数

virtual 修饰的类成员函数称为虚函数。

虽然都用virtual,但是虚函数和虚拟继承没有过多联系。

虚拟继承语法:

        

虚函数语法:

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

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

virtual不能修饰全局函数。虚函数也只是基于类函数的定义。

使用方法就是在返回值类型前面加上一个virtual


2.虚函数的重写(覆盖)

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

“不同的对象完成同一件事情,

得到的结果不同,这便是多态”

      在重写基类虚函数时,派生类的虚函数在不加 virtual 关键字时,虽然也可以构成重写 (
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性 ), 但是该种写法不是很规范,不建议这样使用

3.调用多态

多态的两个条件,缺一不可:

1. 必须通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

可以观察Func发现,他的参数是一个父类的引用,所以可以传派生类st给func(会发生切片)

也可以传父类p给func。都调用了,但是发生多态,也就是编译器通过这个引用本来的大小

在使用两个BuuTicket之前,记得先用public修饰。 

                                   

对于现在的我们来说,可以认为多态是一种智能的、有魔法的技能,两个条件都要成立才能使用这个技能。比如,如果不用基类的引用或指针,就放不出这个技能。

如果将参数Person& p 改成 Person p  ,  不管是基类还是派生类都会被当作基类,调用基类的BuyTicket() , 原因是你传进去的就是一个基类的引用,在多态的魔法消失后,只会把这里当普通基类处理。

                 

或者是用指针也能完成,也能使用出具有魔法的多态。

                                      

如果指定内域的话,就不算多态,而是只会调用指定了内域中的内容。

                                    

特殊规定:只去掉派生类的virtual,依然是多态。

派生类此处的virtual的意思是重写该函数的实现,不同于父类不写virtual,父类不写virtual压根就不是虚函数,一定不满足条件。而派生类不写virtual,相当于就是把从父类继承的虚函数的声明拿下来,重写实现一份。

请注意,重写的是基类虚函数的实现,也就是虚函数的定义,而不是声明。

但是并不建议这种派生类不写virtual的写法,因为有可能有多个平等等级的派生类。

            


4.关于多态和隐藏

不满足多态的条件就是隐藏。

都写virtual的时候,可以理解为多态,也可以理解为一种特殊形态的隐藏。

都不写virtual的时候,连多态的最基本的条件都不满足,所以只能算隐藏。

例,请问以下代码能否使用多态?

                      

虽然满足使用多态的条件:由基类的引用或是指针调该函数

但是这样也是不行的,必须在参数处就接受Person*或者Person&


5. 多态的特殊情况

协变:

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};

当然,Person返回Person Student返回Student也可以。


重点:析构函数的重写(经常出现在面试题中)

上文中我们提到,继承中的析构函数都会被改名字为destructor 这也是为什么不需要我们在子类的析构函数中手动释放父类的原因之一。

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

基类的析构函数都建议设计成虚函数。

否则,如果出现下列情况:

父类的指针或者引用  来new 子类的对象  然后释放的时候是delete  父类的指针 。、

‘复习:delete是对free和析构函数的封装’ 

那么此时会调用父类的析构函数,子类当中如果有开空间的话,父类的析构函数就不能清理子类开出的空间,就会造成内存泄漏。

并且这种错误不会被报错,需要我们注意。

           

但如果我们是将基类的析构函数设计成了虚函数,不管子类的析构函数加不加virtual,都会形成多态,解决这个问题。

可以认为,子类在重写时不是必须加virtual也是为了这个设计的。

也自圆其说的解释了为什么要全部改名为destructor

都把函数名处理成destructor其实是为了填自己的坑,都处理成destructor才能满足虚函数重写的条件。一旦满足虚函数重写的条件,就能通过 父类的指针或者引用” 这一条件,去使用多态,通过基类的指针或引用,该调用基类的析构就基类的析构,该调用派生类的析构就派生类的析构。

小结:

                      

只有多态的时候才使用虚函数。不要乱使用虚函数,虚函数的使用也是会加大空间的占有的。后文会提到这一点。


6. 关键字:final 和 override 用于修饰函数:

final :修饰虚函数,表示该虚函数不能再被重写 ;写在声明后、定义前。
    
此时表示Student的BuyTicket不能被继承。如果在Person的buyticket后面加final,就会语法报错

final还能用于修饰类,final修饰的类不能被继承。

                                      

或者

还有没有办法能使一个类不能被继承?

将基类函数的构造函数写成private,也能间接使该基类不能被继承。

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

class Benz :public Car {
public:
 virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

小结:


7 . 抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数 包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象 。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class car {
public:
	virtual void drive() = 0;//既然是一个不希望被实例化的对象,那么其中的函数自然一般就不会被
//调用,所以一般都不写函数定义
};

  无法实例化:

                            继承了抽象类的类,也包含纯虚函数,也不能实例化出对象。

              


8. 练习

以下代码的输出结果:

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

A和B中的func是构成了重写虚函数的关系

再看调用,p先在子类内部找test(),找不到;然后就去父类内部找,找到了,调用test()

难点一:此时调用test()的是在父类里面,因此此时的this是A* ,  而this调用了func()

A*作为父类指针调用了一个重写的虚函数,多态的条件齐了。

多态是:

                        

所以调用的是子类的func。

难点二:但是因为重写是重写虚函数定义而非声明,缺省参数还是用的基类的声明中的缺省参数,所以答案是B->1

重写 虚函数的 实现  ;  派生类的重写是基类的声明和重新写的定义。


题目二:写出sizeof(Person)的结果。       

按照补齐的规则,应该是8个字节,但实际运行显式是12个字节

因为虚函数需要放进虚函数表,会存一个函数的指针,一共12个字节。

可以观察到,除了_x和_c  还有一个虚函数表指针(vvirtualf代表function)

占四个字节,共12个。


 

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

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

相关文章

短剧APP系统搭建,短剧市场的发展机遇

短剧作为近几年内发展快速的行业&#xff0c;一直深受大众的欢迎&#xff0c;各种让观众上头的短剧层出不穷&#xff0c;深深吸引着大众。短剧的巨大发展前景也吸引了大量资本涌入市场&#xff0c;目前&#xff0c;短剧入局者也都获得了不菲的经济收益&#xff01; 随着短剧行…

C++下标+【】、迭代器、范围for、迭代器对于其他容器都是通用的、迭代器可以更好的跟算法配合、rbegin和rend函数、const修饰的迭代器等的介绍

文章目录 前言一、 下标 【】二、 迭代器1.begin2. end3. 使用迭代器遍历string类对象 三、范围for(语法糖)五、迭代器对于其他容器都是通用的六、迭代器可以更好地跟算法配合七、 rbegin 和 rend函数八、 const 修饰的迭代器总结 前言 C下标【】、迭代器、范围for、迭代器对…

JavaEE---Spring MVC(5)

MVC学习小案例3 留言板案例 后端代码 测试 点击刷新的时候页面的这些记录仍在 一个小tips 我们在日常中写的时候会经常写到get和set方法,这会使整个代码看起来非常多不好看,这里我们引入一个新的依赖解决这个问题 引入LomBok依赖 那要是个别情况下我们不想获取他的ge…

高集成度双通道差分式电容型传感芯片-MC11

工采电子代理的MC11S、MC11T是一款高集成度双通道电容型传感芯片&#xff0c;芯片直接与被测物附近的差分电容极板相连&#xff0c;通过谐振激励并解算测量微小电容的变化。激励频率在0.1~20MHz范围内可配置&#xff0c;其频率测量输出为16bit数字信号&#xff0c;对应的电容感…

Ventoy启动盘制作

然后直接将系统的ISO镜像直接拷贝进去&#xff0c;就能直接使用

69页PPT全面预算管理体系的框架与落地

一、明确企业战略目标企业战略目标是预算指标体系确立的根本出发点。它为预算指标的设定提供了方向和指导。 深入分析企业长期发展规划 企业需要对自身的长期发展规划进行全面、深入的分析。这包括对市场趋势、行业竞争态势、技术发展方向等外部环境因素的研究&#xff0c;以…

AI技术颠覆游戏开发:谷歌DeepMind GameNGen实时生成《DOOM》探秘

引言 近年来&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;在图像和视频生成领域取得了巨大突破。然而&#xff0c;谁能想到&#xff0c;这项技术正逐渐渗透进游戏开发领域&#xff0c;且潜力巨大。2023年8月29日&#xff0c;谷歌DeepMind发布了名为《扩散模型是实时…

【舍入,取整,取小数,取余数丨Excel 函数】

数学函数 1、Round函数 Roundup函数 Rounddown函数 取整&#xff1a;(Int /Trunc)其他舍入函数&#xff1a; 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字&#xff0c;保留几位小数)&#xff08;四…

解除网站禁用右键 解除禁用选择方法 并允许复制

限制我复制&#xff0c;太恶心了&#xff0c;别用技术作恶&#xff01;&#xff01;&#xff01; 一般HTML网站禁止右键选择的方法 <body ondragstart"return false" oncontextmenu"return false" onselectstart"return false">解除网站…

遇到“msvcp120.dll丢失”的错误提示?来看看msvcp120.dll丢失的解决方法都有哪些?

遇到“msvcp120.dll丢失”的错误提示可能会让人感到焦虑&#xff0c;尤其是当你尝试运行某个应用程序或游戏时突然接收到这样的消息。​msvcp120.dll​是Microsoft Visual C 2013 Redistributable Package 中的一个文件&#xff0c;主要负责C标准库中的功能&#xff0c;比如输入…

C++第四十六弹---解锁多线程编程的奥秘:<thread>库深入探索

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1 线程库 1.1 thread类的简单介绍 1.2 线程函数参数 1.3 原子性操作库(atomic) 1.4 lock_guard与unique_lock 1.4.1 mutex的种类 1.4.2 loc…

python-小理的三角形

题目描述 小理有一个数组长度大小为 n &#xff0c;数组中有 n 个正整数。 现在小理请你从其中选出三个元素&#xff08;注意选择元素的下标不能相同&#xff0c;但是其值可以相同&#xff09;组成一个三角形。 无法做到&#xff0c;请输出一行一个字符串"No solution&quo…

SQL进阶技巧:每年在校人数统计 | 区间重叠问题

目录 0 问题分析 1 数据准备 2 问题分析 3 小结 区间重叠问题 0 问题分析 有一个录取学生人数表 in_school_stu&#xff0c;记录的是每年录取学生的人数及录取学生的学制&#xff0c;计算每年在校学生人数。 1 数据准备 create table in_school_stu as ( select stack(5,…

【平渊网络】副业项目拆解:视频借鉴式搬运项目 | 搞笑视频跨平台 “借鉴式” 搬运项目思路 | 抖音防查重机制基础

目录 项目介绍 实操&#xff1a;账号准备 素材制作教学 防查重机制基础 项目介绍 只分享实操干货&#xff0c;不浪费时间。如果你没有2台手机&#xff0c;或者手机不支持分身就不用往下读了&#xff0c;这个项目大概率是做不出流量的。 项目是情感聊天对话的变种&#xff…

C++ DLL DEMO

头文件dlltest.h #pragma once #include "pch.h" #include <iostream> #include <fstream> #include <iomanip> #include <string> #include <bitset>extern "C" __declspec(dllexport) void debugService(uint32_t debugF…

【全网最全】2024年数学建模国赛B题31页完整建模过程+成品论文+matlab/python代码等(后续会更新

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 2024数学建模国赛B题 【全网最全】2024年数学建模国赛B题31页完整建模过程成品论文matlab/python代码等&#xff08;后续会更新「首先来看看目前已有的资料…

【计算机图形学】《五讲速通CG重点》第三讲-Rendering

如果这篇文章对你有帮助&#xff0c;欢迎点赞与收藏~ 目录 Rasterization 1 Points 2 Line 2.1 DDA&#xff08;差分&#xff09; 2.2 Bresenham’s algorithm: 2.3 Mid-point algorithm (0-45) 3 Triangles 4 Polygon 5 Triangulation 6 Interpolation 7 Anti-alia…

运动耳机哪个牌子的好?五大选购技巧助你轻松购买

作为一名专注运动装备的测评专家&#xff0c;我曾深入体验了多款骨传导耳机&#xff0c;发现市场上不少品牌并未如广告所宣称的那般出色&#xff0c;部分低质或不专业的骨传导耳机在使用时不仅舒适度欠佳&#xff0c;还可能因设计缺陷导致声音泄露严重&#xff0c;非常影响使用…

共赢算力未来,华宇TAS应用中间件获“鲲鹏原生开发认证”

9月2日&#xff0c;华为在北京中关村国家自主创新示范区会议中心举办了以“鲲鹏原生&#xff0c;力算未来”为主题的鲲鹏原生开发伙伴圈层活动&#xff0c;旨在聚集鲲鹏生态合作伙伴&#xff0c;搭建交流平台&#xff0c;加强产业圈各方紧密合作&#xff0c;抓住产业机遇&#…

Canvas艺术之旅:了解几个绘制基本图形的 API

了解几个绘制基本图形的 API Canvas 是 HTML5 提供的绘画 API&#xff0c;可以用于在 Web 页面上绘制各种基本图形。本文介绍一些 Canvas 绘制基本图形的 API&#xff1a; 前置条件 注意&#xff1a;本文章所提供的代码示例默认已经进行了 canvas 元素定义&#xff0c;DOM 获…