【C++】多态(上)

news2024/9/30 10:51:27

在这里插入图片描述
个人主页~


多态

  • 一、多态的概念
  • 二、多态的定义以及实现
    • 1、多态的构成条件
    • 2、虚函数
    • 3、虚函数的重写
      • 虚函数重写的两个特殊情况
        • ①协变
        • ② 析构函数的重写
    • 4、C++11的override和final
      • (1)final
      • (2)override
    • 5、重载、重写、隐藏的对比
  • 三、抽象类
    • 1、概念
    • 2、接口继承和实现继承
  • 四、多态的原理
    • 1、虚函数表

一、多态的概念

用大白话讲就是完成某个行为,不同对象去完成会产生不同状态,C++多态就是在不同继承关系的类对象,去调用同一函数,产生了不同的行为

二、多态的定义以及实现

1、多态的构成条件

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

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

2、虚函数

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

class A
{
public:
	virtual void B()
	{
		cout << "virtual void B()" << endl;
	}
};

B函数是一个虚函数,这里的virtual与虚拟继承的virtual没有关系,只是它们表示相同的意思,关键字用在不同的对象上有不同的效果

3、虚函数的重写

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

class A 
{
public:
	virtual void C()
	{
		cout << "A : virtual void C()" << endl;
	}
};

class B : public A
{
public:
	virtual void C()
	{
		cout << "B : virtual void C()" << endl;
	}
};

void test(A& a)
{
	a.C();
}

int main()
{
	A a;
	B b;
	test(a);
	test(b);

	return 0;
}

在这里插入图片描述

虚函数重写的两个特殊情况

①协变

所谓协变就是基类与派生类虚函数返回值类型不同

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

class A 
{};
class B : public A 
{};

class C 
{
public:
	virtual A* f() 
	{
		return new A; 
	}
};
class D : public C 
{
public:
	virtual B* f() 
	{
		return new B; 
	}
};
② 析构函数的重写

析构函数的重写的特征是基类与派生类析构函数的名字不同

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

class A 
{
public:
	virtual ~A() 
	{
	 cout << "~A()" << endl; 
	}
};
class B : public A 
{
public:
	virtual ~B() 
	{
	 cout << "~B()" << endl; 
	}
};

int main()
{
	A* pa = new A;
	B* pb = new B;

	delete pa;
	cout << endl;
	delete pb;
	return 0;
}

在这里插入图片描述
这样的设计巧妙的将父子类的析构函数关系在一起,当然这其实是一个设计失误,这个多态或者说继承的概念是在析构函数的概念之后产生的,所以作为第一个面向对象的语言,就只能通过打补丁,也就是编译器将两个析构函数转化为destructor()保证它们同名

4、C++11的override和final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写

(1)final

final用来修饰虚函数,表示该虚函数不能再被重写
在这里插入图片描述

在这里插入图片描述

(2)override

override用来检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
在这里插入图片描述

在这里插入图片描述

5、重载、重写、隐藏的对比

重写也叫覆盖,隐藏也叫重定义

在这里插入图片描述

三、抽象类

1、概念

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

class A
{
public:
	virtual void D() = 0;
};
class B :public A
{
public:
	virtual void D()
	{
		cout << "BBBBBBBBBB" << endl;
	}
};
class C :public A
{
public:
	virtual void D()
	{
		cout << "CCCCCCCCCC" << endl;
	}
};
void Test()
{
	A* pb = new B;
	A* pc = new C;
	pb->D();
	pc->D();
}

在这里插入图片描述

2、接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现,虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口,所以如果不实现多态,不要把函数定义成虚函数

派生类继承基类虚函数接口的一个实例

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

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

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

在这里插入图片描述
这里就是因为派生类继承基类接口,重写函数,但是接口还是基类的,函数是被重写的,所以这里给的缺省值应该取基类A类func函数中的

四、多态的原理

1、虚函数表

class A
{
public:
	virtual void func1()
	{
		cout << "virtual void func1()" << endl;
	}
private:
	int _a;
};

int main()
{
	cout << sizeof(A) << endl;
	return 0;
}

在这里插入图片描述
我们发现A类的大小为8bytes,除了私有成员_a以外还有一个_vtfptr的指针,我们把它叫做虚函数表指针,一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表简称虚表
在这里插入图片描述

class A
{
public:
	virtual void Func1()
	{
		cout << "A::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "A::Func2()" << endl;
	}
	void Func3()
	{
		cout << "A::Func3()" << endl;
	}
private:
	int _a = 1;
};

class B : public A
{
public:
	virtual void Func1()
	{
		cout << "B::Func1()" << endl;
	}
private:
	int _b = 2;
};

int main()
{
	A a;
	B b;
	return 0;
}

在这里插入图片描述
派生类对象b中也有一个虚表指针,b对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就是存在部分的另一部分是自己的成员

基类a对象和派生类b对象虚表是不一样的,这里我们发现Func1完成了重写,所以b的虚表中存的是重写的B::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖,重写是语法的叫法,覆盖是原理层的叫法

另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会放进虚表

虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr

总结一下派生类的虚表生成:
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后

注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中,另外对象中存的不是虚表,存的是虚表指针,虚表在VS下存在于代码段


今日分享就到这里了~

在这里插入图片描述

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

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

相关文章

普中51单片机

参考&#xff1a;51单片机快速入门教程2022&#xff08;普中51开发板A2新版&#xff09;--绍兴文理学院元培学院《单片机原理与应用》课程建设_哔哩哔哩_bilibili 1.以管理员启动&#xff0c;破解

【开源免费】基于SpringBoot+Vue.JS校园资料分享平台(JAVA毕业设计)

本文项目编号 T 059 &#xff0c;文末自助获取源码 \color{red}{T059&#xff0c;文末自助获取源码} T059&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

【抖音矩阵源码开发:短视频矩阵SEO技术深度解析与实践指南】

开发部署短视频矩阵源码的主要步骤如下&#xff1a; 开发和部署短视频矩阵平台涉及一系列精细化的技术步骤&#xff0c;旨在确保系统的高效构建与稳定运行。该流程起始于开发环境的精心部署&#xff0c;涵盖PHP环境的搭建、集成开发环境&#xff08;IDE&#xff09;的配置以及…

STM32编码器接口笔记

1. 引言 在现代控制系统中&#xff0c;编码器扮演着非常重要的角色。它就像一个精密的测量工具&#xff0c;可以告诉我们机械部件的位置和运动状态。在STM32微控制器中&#xff0c;编码器接口可以轻松地与各种编码器连接&#xff0c;实现精确的控制。我将在这里探讨STM32编码器…

string类的使用(上)

目录 1.string类的概念 2.string的构造函数&#xff08;实现初始化&#xff09; 3.对string类容量获取和操作 4.string类对象的访问和遍历 4.1operator[] 4.2begin和end 4.3 rbegin 和 rend 4.4迭代器分类 4.5范围for&#xff08;自动实现遍历&#xff09; 5.string类…

打造高效舒适的气膜网球馆—轻空间

气膜网球馆&#xff0c;作为现代运动设施的创新选择&#xff0c;其成本构成涵盖多个重要方面&#xff0c;确保为运动者提供最佳体验。 一、膜材选择 膜材是气膜网球馆的核心&#xff0c;品质不同直接影响成本。高品质膜材不仅增强了耐用性&#xff0c;也能有效阻挡外界气候影响…

【展会回顾】阿尔泰科技2024上海工博会精彩瞬间,圆满收官 ,感恩遇见 ,共赴新程!

9月24日&#xff0c;以“工业聚能 新质领航”为主题的第二十四届中国国际工业博览会&#xff08;以下称“工博会”&#xff09;在国家会展中心&#xff08;上海&#xff09;举办&#xff0c;展示了一批代表中国工业领域的最新技术与前沿成果。 本届工博会的主题“工业聚能&…

Pycharm 本地搭建 stable-diffusion-webui

一、下载工程源码 Github官方连接 https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、Pycharm打开工程 1、设置环境 文件-设置-项目-Python解析器-添加解释器-添加本地解释器 Conda环境-创造新环境-Python版本3.10 注意一定要选择Python3.10版本&#xff0c;否…

uni-app canvas文本自动换行

封装 支持单行文本超出换行。多行文本顺位排版 // 填充自动换行的文本function fillFeedText({ctx, text, x, y, maxWidth, lineHeight, color, size}) {// 文本配置ctx.setFontSize(size);ctx.setFillStyle(color);// 计算文本换行宽高&#xff0c;换行逻辑const words text…

JavaScript中的函数定义

第8章 函数 在JS中函数即对象&#xff0c;程序可以随意操控他们。可以把函数赋值给变量&#xff0c;或者作为参数传递给其他函数。因为函数就是对象&#xff0c;所以可以给他们设置属性&#xff0c;甚至调用他们的方法。 JavaScript的函数可以嵌套在其他函数中定义&#xff0…

初识C语言(四)

目录 前言 十一、常见关键字&#xff08;补充&#xff09; &#xff08;1&#xff09;register —寄存器 &#xff08;2&#xff09;typedef类型重命名 &#xff08;3&#xff09;static静态的 1、修饰局部变量 2、修饰全局变量 3、修饰函数 十二、#define定义常量和宏…

5250 Fantasy RPG Icons Pack 幻想RPG游戏图标合集

这里是集合 - 幻想RPG图标 它包括50个主题图标。文件:PNG。 在档案中,带有背景的图标(512512)和没有背景的单个对象(大小取决于主题)。仅具有背景技能的技能。 2024年2月更新(+500个图标): RPG蔬菜图标; RPG游戏中的蜘蛛战利品图标; RPG海战战利品图标; RPG怪物战利…

匿名管道 Linux

管道 首先自己要用用户层缓冲区&#xff0c;还得把用户层缓冲区拷贝到管道里&#xff0c;&#xff08;从键盘里输入数据到用户层缓冲区里面&#xff09;&#xff0c;然后用户层缓冲区通过系统调用&#xff08;write&#xff09;写到管道里&#xff0c;然后再通过read系统调用&…

新手上路:Anaconda虚拟环境创建和配置以使用PyTorch和DGL

文章目录 前言步骤 1: 安装 Anaconda步骤 2: 创建新的 Anaconda 环境步骤 3: 安装最新版本的 PyTorch步骤 4: 安装特定版本的 PyTorch步骤 5: 安装最新版本的 DGL步骤 6: 安装特定版本的 DGL步骤 7: Pycharm中使用虚拟环境解释器第一种情况&#xff1a;创建新项目第二种情况&am…

Linux Shell编程最重要的十个核心概念

对于每个Linux学习者而言&#xff0c;Shell编程的重要性不言而喻。它是连接用户与系统操作的桥梁&#xff0c;能够培养命令行的熟练度和解决问题的能力。通过Shell编程&#xff0c;学习者可以深入理解系统管理和日常任务自动化的精髓&#xff0c;提高工作效率。此外&#xff0c…

应急响应(1)-同事电脑

一、现象 重要时间节点&#xff0c;同事反馈桌面有鼠标移动、随机断网&#xff1b;发现登录账户多了一个&#xff0c;由于电脑长时间没有更改过密码&#xff0c;导致忘记密码无法登录。随机联系进行应急处理。 二、排查 2.1、密码重置/删除 由于同事忘记密码导致无法进行登录…

Linux文件系统入门详解-总结大章

我们先看一张图&#xff1a; 这张图大体上描述了 Linux 系统上&#xff0c;应用程序对磁盘上的文件进行读写时&#xff0c;从上到下经历了哪些事情。 这篇文章就以这张图为基础&#xff0c;介绍 Linux 在 I/O 上做了哪些事情。 文件系统 什么是文件系统 文件系统&#xff0…

EKF学习笔记

扩展卡尔曼滤波&#xff0c;也就是EKF&#xff0c;常用于在动态系统中对状态的估计。比如&#xff0c;在机器人领域&#xff0c;EKF则常常用于对状态&#xff08;位置&#xff0c;方向&#xff09;的估计&#xff0c;也就是我们常说的数据融合&#xff0c;结合运动模型和观测数…

vscode使用yarn 启动vue项目记录

第一次启动yarn项目&#xff0c;这个是公司的老项目&#xff0c;遇到了点问题&#xff0c;记录下首先是我一般使用的是npm命令&#xff0c;所以没有安装yarn vscode安装yarn vscode进入到该项目文件夹下&#xff0c;输入命令&#xff1a;npm install -g yarn 安装成功后&…

河南人社厅:注册满两年可按条件认定副高

河南省工程系列建筑专业中高级职称 申报评审标准 总则 一、为培养造就高素质、社会化的工程建设领域专业技术人才队伍&#xff0c;充分发挥工程建设领域专业技术人才在科技发展和经济建设中的作用&#xff0c;推动技术创新、科技成果转化和实现高新技术产业化&#xff0c;根据…