多态与虚函数

多态与虚函数

  • 多态的引入
  • 多态与虚函数
    • 多态
    • 编译时多态
    • 运行时多态
  • 多态的原理
  • 静态联编和动态联编

多态的引入

学过C++继承的话应该都知道在继承中存在一种菱形继承,假设存在一个类(person),其派生出两个子类,分别是student类和Migrant类,我们但是这两个子类共同同派生出一个MigStu的类,我们可以通过画图来理解一下,
在这里插入图片描述
我们画出他的继承图会发现其是一个菱形,我们再画出他的内存分布图在这里插入图片描述
会发现存在两个person类,那么其中的公共成员变量就会发生冲突,比如学生是男性,打工人是女性,那么我们的在校打工人是男是女,这很显然发生了冲突。为了解决这个问题C++引入了virtual关键字,在person中我们加入virtual关键字之后,就会只在类中生成一个person对象,在学生和打工人的结构中原本存放person的地方会存在一个指针,指向我们的person类,也就解决了冗余的问题。但是菱形继承的底层实现极为复杂,所以现在很少设置这种菱形继承。而我们使用virtual继承基类,被称为虚基类,但是其并非基类是虚的,而是继承方式是虚的,而使用多态时,便使用的是虚函数,同样时virtual关键字。

多态与虚函数

多态

多态性是面向对象程序设计的关键技术之一。若程序设计语言不支持多态性,则不能称为面向对象语言。多态性是考虑在不同层次的类中,同名的成员函数之间的关系。函数的重载,运算符的重载,属于编译时的多态性。以类的虚成员函数作为基础运行时的多态性是面向对象程序设计的标志性特征。
多态分为编译时多态和运行时多态

编译时多态

编译时多态也就是函数重载,函数名相同,参数不同,其本质上是代码在编译时期对函数进行了名字粉碎技术,将函数名,参数,返回值做了一个粉碎,导致其在运行时编译器认为其名字不相同从而实现函数重载,

int Max(int a,int b) {return a>b?a:b;}
char Max(char a,char b) {return a>b?a:b;}
double Max(double a,double b) {return a>b?a:b;}

运行时多态

在一些类中会存在一些共同的特性,为此会设置一个基类将所有的共性存放到这个类中,并将这些共性设置成虚函数,在派生类中再一次实现具体的操作,这样就避免了代码的复用,并且使得代码更容易维护,也就是说以后有其他类的话直接加入派生类中即可。在调用时必须使用指针或者引用才可以将虚函数绑定到派生类的对象上重写虚函数。

#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
};
// 程序的主函数
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
 
   // 存储矩形的地址
   shape = &rec;
   // 调用矩形的求面积函数 area
   shape->area();
 
   // 存储三角形的地址
   shape = &tri;
   // 调用三角形的求面积函数 area
   shape->area();
   
   return 0;
}

使用多态时我们必须存在三个条件:

  • 必须是公有继承
  • 必须加入virtual
  • 必须使用指针或者引用来调用
    定义虚函数需要注意:
  • 派生类中定义虚函数必须与基类中的虚函数同名,同参数表,同返回类型。否则会被认为是同名覆盖,不具有多态性,但存在一个例外:基类中返回基类指针,派生类中返回派生类指针(协变)。
  • 只有类的成员函数才能说明是虚函数,因为虚函数仅使用于有继承关系的类对象。有缘函数和全局函数都不能作为虚函数
  • 构造函数和拷贝构造函数不能作为虚函数,拷贝函数和拷贝构造函数是设置虚表指针。
  • 析构函数可以定义为虚函数,构造函数不能定义为虚函数,因为在调用构造函数时对象还没有完成实例化(虚表指针没有设置)。在基类中及其派生类中都动态分配内存空间时,必须把析构函数定义为虚函数,实现撤销对象的时的多态性。
  • virtual之正在函数声明前加,不能再函数定义时加入,正确的定义必须不包括virtual,函数默认参数值也必须在声明时加入,定义时不能加入。

多态的原理

多态的原理就是虚函数表简称为虚表,虚表就是虚函数指针的集合,虚函数指针表本质上是一个存储虚函数指针的指针数组,这个数组的首元素之上存储RTTI(运行时类型识别信息的指针),从数组下标开始依次存储虚表地址,最后放了一个nullptr,虚表存放于只读数据段,其在编译时确定,并且一个类只存在一个虚表。

class object{
private: int value;
pubic:
object(int X = 0) :value(x) {}
virtual void add() { cout << "object: :add()" << end1; }
virtual void fun() { cout << "object: :fun()" << endl; }
virtual void print() const
{ cout << "object::printO" << end1; }
};
class Base: pub1ic object
private:
int num;
public:
Base(int x = 0) :object(x) ,num(x + 10) {}
virtual void add() { cout << "Base: :add()" << end1; }
virtual void fun() { cout << "Base: :fun()" << end1; }
virtual void show() { cout << "Base::show()" << end1; }
};
class Test : public Basve{
private:
int count;
public:
Test(int x = 0) :Base(x), count(x + 10) {}
virtual void add() { cout << "Test: :add()" << end1; }
virtual void print() const
{ cout << "Test: :print()" << end]; }
virtual void show() { cout << "Test: :show()" << end1; }
};
int main(){
object *op = nu11ptr;
objece obj;
Base base;
Test test;
op = &test;
op->print() ;
return 0;
}

我们观察上面代码,化出虚表内存分布图以及虚表图如下:
在这里插入图片描述
如图便是三个类的虚表,实在编译时就确定了的,例如obj派生出了Base类,所以Base的虚表就是将obj的虚表拷贝了一份然后进行同名覆盖,而在创建对象的时候,我们以创建Test对象为例,创建该对象首先创建Base,而创建Base需要创建obiect,而当类中有虚函数时类的大小就会多4(32位)字节,用于存放指向虚表的指针,创建Test时,指针首先指向object的虚表,然后指向Base的虚表,最后指向Test的虚表,根据其构建顺序。这个过程是在运行时进行的,虚表创建在编译时。

静态联编和动态联编

其实两者的差异就在于关联(函数实现和函数调用关联)的时期不一样,静态联编在编译时就确定了关联,而动态联编是在运行时确定的关联关系。
静态关联就是用直接用对象调用函数,动态联编就是用指针调用函数,注意虚表指针是否指向虚表。
C++中函数重载,函数模板都是静态联编,使用指针引用都是动态联编

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

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

相关文章

ArcGIS栅格重采样与算法选择

本文介绍在ArcMap软件中&#xff0c;实现栅格图像重采样的具体操作&#xff0c;以及不同重采样方法的选择依据。 首先&#xff0c;如下图所示&#xff0c;是我们待重采样的栅格图像的属性界面。其中&#xff0c;可以看到此时栅格像元的边长为0.4867左右&#xff08;由于图层是地…

【爬虫逆向】Python加密算法大揭秘:应用场景与实现技巧

前言&#xff1a;​ 在我们进行js逆向的时候. 总会遇见一些我们人类无法直接能理解的东西出现. 此时你看到的大多数是被加密过的密文.今天在这里教大家各种加密的逻辑。 Python助学大礼包点击跳转获取 目录 一、一切从MD5开始二、URLEncode和Base64三、对称加密四、非对称加密…

JS逆向 -- 分析某站aid、cid、w_rid和sid的加密过程

接上节课内容 JS逆向 -- 分析某站buvid3和_uuid的加密过程 JS逆向 -- 分析某站b_lsid值加密过程 一、清除cookie信息&#xff0c;刷新网页&#xff0c;ctrlf搜索sid&#xff0c;这样找到的数据是在url里或者响应信息里面&#xff0c;全局搜索找到的一般都是在js里面的数据&a…

Linux实操篇---常用的基本命令4(磁盘查看和分区类)

一、磁盘查看和分区类 du查看文件和目录占用的磁盘空间 du&#xff1a;disk usage 磁盘占用情况 基本语法&#xff1a; du 目录/文件 显示目录下每个字母里的磁盘使用情况选项说明&#xff1a; 选项功能-h以人们较易阅读的GBytes&#xff0c;MBytes&#xff0c;KBytes等…

【AIGC】阿里云服务器配置stable-diffusion-webui

阿里云服务器部署SD全流程, 正在更新&#xff01;&#xff01;&#xff01; 购买阿里云实例开始部署开始运行安装插件中文插件从civitai上下载模型, 加载并利用其绘图 购买阿里云实例 我感觉应该不止我一个&#xff0c;点进阿里云的官网后&#xff0c;发现里面的内容太多&…

C++ 仿函数(一)

目录 一、仿函数是什么&#xff1f; 二、仿函数的特点 1.仿函数在使用时&#xff0c;可以像普通函数那样调用, 可以有参数&#xff0c;可以有返回值 2.仿函数超出普通函数的概念&#xff0c;可以有自己的状态 ​编辑3.仿函数可以作为参数传递。 三、谓词 一元谓词示例&a…

Azure API 管理缺陷突出了 API 开发中的服务器端请求伪造风险

微软最近修补了其 Azure API 管理服务中的三个漏洞&#xff0c;其中两个漏洞启用了服务器端请求伪造 (SSRF) 攻击&#xff0c;这些攻击可能允许黑客访问内部 Azure 资产。 概念验证漏洞用于突出开发人员在尝试为自己的 API 和服务实施基于黑名单的限制时可能犯的常见错误。 W…

4月更新 | Visual Studio Code Python

我们很高兴地宣布2023年4月版 Visual Studio Code 的 Python 和 Jupyter 扩展现已推出&#xff01; 此版本包括以下改进&#xff1a; Data Wrangler 可供 Visual Studio Code Insiders 使用移动符号重构Create Environment 按钮嵌入依赖文件扩展作者的环境 APIPython 环境的内…

BTC交易费激增,LTC活跃地址数飙升! BRC-20爆火背后,区块链网络经历了什么?

BRC-20 代币和 Ordinals 协议的日益普及推动了对比特币区块空间的需求&#xff0c;比特币区块链的费用已飙升至两年来的高点。 BRC-20代币标准在 Ordinals 协议上运行。Ordinals 允许用户通过将对数字艺术的引用写入基于比特币的小型交易中&#xff0c;来将数据嵌入比特币区块…

MySQL体系架构

一、 MySQL体系架构 MySQL体系架构可分为物理架构和逻辑架构。 1、MySQL物理体系架构 1.1 配置文件 auto.cnf: 配置了MySQL Server的UUIDmy.cnf: MySQL的配置文件 1.2 其他重要文件 -basedirdir_name: MySQL安装的二进制文件目录-datadirdir_name: MySQL的数据目录和-pid-…

特征选择与特征提取

目录 一、 特征选择1、特征2、特征选择3、扩展——特征选择算法(有兴趣和精力可了解)拓展--完全搜索:拓展--启发式搜索:拓展--随机搜索:拓展--遗传算法: 二、 特征提取三、特征提取主要方法——PCA(主成分分析)1、PCA算法是如何实现的&#xff1f;PCA--零均值化&#xff08;中心…

抖音未来的发展趋势|成都欢蓬信息

抖音未来的发展趋势&#xff0c;近年来随着互联网技术的发展&#xff0c;小视频app也逐渐走入大家的日常生活中&#xff0c;闲着的时候打开手机抖音APP&#xff0c;就可以刷到世界各地人们分享的视频和直播&#xff0c;下面一起看看抖音未来的发展趋势 一、抖音的现状   据权…

某IC交易网 js逆向解析学习【2023/05/16】

文章目录 文章目录 文章目录前言网址目标参数确认加密点cookie解密第一步hex1算法解析rind和rnns完结撒花前言 可以关注我哟,一起学习,主页有更多练习例子 如果哪个练习我没有写清楚,可以留言我会补充 如果有加密的网站可以留言发给我,一起学习共享学习路程 如侵权,联系我…

springboot + vue3实现视频播放Demo

文章目录 学习链接前言ffmpeg安装ffmpeg配置环境变量分割视频文件 后台配置WebConfig 前端代码video.js示例安装依赖视频播放组件效果 Vue3-video-play示例安装依赖main.js中使用视频播放组件效果 学习链接 ffmpeg官网 长时长视频java存储及vue播放解决方法 【 攻城略地 】vue…

让Chat-GPT成为你的微信小助理

前言 最近公司裁员风波&#xff0c;很不幸成为了裁员名单中的一员&#xff1b;此时又恰逢一波AIGC浪潮&#xff0c;首当其冲的就是GPT这样的大语言模型的诞生&#xff0c;是整个AI领域的一个质的飞跃。正好在这样一个空挡期&#xff0c;我就基于Chat-GPT 做了一些深入的实践&a…

Cannot read properties of null (reading ‘content‘)报错解决

项目是用vue3webpack&#xff0c;始终启动不成功~ 一、问题报错 二、报错解决尝试总结 &#xff08;1&#xff09;首先尝试的是因为我近期在做vite3vue3的需求把node版本升到了 16.17.1 猜测是不是node版本影响的 node版本切了14.15.3&#xff0c;16.17.1&#xff0c;以及很…

Oracle EBS Interface/API(48)- AP发票取消API

快速参考 参考点内容功能导航N: AP->发票->录入->发票并发请求None基表AP.AP_INVOICES_ALLAPI参考下面介绍错误信息表None接口FormNone接口RequestDebug ProfileNone详细例子参考如下实例官方文档None数据验证包None用户界面 Path:AP->发票>录入>发票->活…

全网火爆,性能测试从0到1分析需求到实战详解,冲出高级测试岗...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、制定测试目的 …

国产ChatGPT命名图鉴

很久不见这般热闹的春天。 随着ChatGPT的威名席卷全球&#xff0c;大洋对岸的中国厂商也纷纷亮剑&#xff0c;各式本土大模型你方唱罢我登场&#xff0c;声势浩大的发布会排满日程表。 有趣的是&#xff0c;在这些大模型产品初入历史舞台之时&#xff0c;带给世人的第一印象其…

做SSM项目的步骤和优化

SSM框架整合 这里说的SSM整合&#xff0c;主要说的是Spring和mybatis之间的整合。因为spring和springMVC都是spring生态系统中的框架&#xff0c;所以spring和springMVC之间的整合是无缝的整合&#xff0c;即&#xff0c;我们在不知不觉中&#xff0c;其实spring和springMVC已…