C++基础 虚函数

news2024/11/25 5:02:45

参考

顺便记录下写的比较好的博客
C++ Primer Plus (第6版)
C++虚函数表
C++内存模型
关于vtordisp知多少?
【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)
C++ 虚函数表剖析

虚函数

静态联编: 在编译过程中函数实现与函数关联
动态联编: 在程序执行阶段函数实现和调用关联

基类中没有把函数声明为虚, 根据指针类型调用调用对应的函数实现, 编译器对非虚方法使用静态联编
基类中把函数声明位虚, 根据对象类型调用对应的函数实现, 编译器对虚方法使用动态联编

向上强制转换: 派生类的引用或指针转换为基类引用或指针
向下强制转换: 基类的引用或指针转换为派生类的引用或指针(不能隐式转换, 会导致不安全)

**虚函数:**在基类方法的神明中使用关键字virtual可使改方法在基类以及所有派生类(包括从派生类派生出来的类)中是虚的

使用指向对象的引用或指针来调用虚方法, 程序会使用对象类型定义的方法, 而不是引用或指针类型定义的方法

#include <iostream>

using namespace std;
class A {
public:
	A(int a):m_a(a) {
		cout << "A地址:" << this << endl;
	}

	virtual void Show() {
		cout << "A::Show()" << endl;
	}
public:
	int m_a;
};

class B : public A{
public:
	B(int a, int b):A(a), m_b(b) {
		cout << "B 地址:" << this << endl;
	}
	
	virtual void Show() {
		cout << "B::Show()" << endl;
	}
public:
	int m_b;
};

void fr(A& rb) {
	rb.Show();
}

void fp(A* rb) {
	rb->Show();
}

void fv(A a) {
	a.Show();
}

int main() {
	A a(10);
	B b(10, 11);
	fr(a);		// 使用 A::Show()
	fr(b);		// 使用 B::Show()
	fp(&a);		// 使用 A::Show()
	fp(&b);		// 使用 B::Show()
	fv(a);		// 使用 A::Show()
	fv(b);		// 使用 A::Show()
}

虚函数的注意事项

虚函数内存和执行速度方面有一定成本:

  • 每个对象都会增大, 增大存储地址的空间
  • 每个类, 编译器都创建一个虚函数地址表
  • 对于每个函数调用, 都需要执行一项额外的操作, 表中查地址

构造函数
构造函数不能是虚函数, 创建派生类对象, 将先调用基类的构造函数, 在调用派生类的构造函数

析构函数
析构函数需要为虚函数, 默认静态联编, delete会调用指针类型的析构函数, 释放派生类对象中基类指向的内存, 不会释放派生类指向的内存
析构函数时虚的, 则会先调用对象析构函数先释放派生类指向的内存, 再调用基类析构函数释放基类指向的内存

友元函数
友元不能是虚函数, 友元不是类成员, 只有成员才能是虚函数, 可以用过友元函数使用虚成员函数来解决

派生类重新定义函数, 不会生成函数的两个重载版本, 会隐藏基类所有同名的类方法

  1. 重新定义继承的方法, 确保与原来原型完全相同, 如果返回类型是基类引用或指针, 则可以修改为指向派生类的引用或指针, 这种特性称为返回类型协变(convariance of return type)
  2. 基类声明被重载, 则应再派生类中重新定义所有的基类版本, 只定义一个, 则另外两个版本将被隐藏
    在这里插入图片描述

虚函数表

编译器处理虚函数是给对象添加隐藏成员, 隐藏成员中保存一个指向函数地址数组的指针, 这个数组称为虚函数表(virtual function table, vtbl)
虚函数表存储了为类进行声明的虚函数的地址
在这里插入图片描述
1. 同类对象的虚函数表

#include <iostream>

using namespace std;

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

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

int main(){
	A a;
	A a1;
	return 0;
}

在这里插入图片描述
会发现同一类的对象用的虚函数是相同的, 都指向一个指针

2. 继承(无虚函数覆盖)

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}
	
	virtual void func1() {
		cout << "A func1" << endl;
	}
	
	virtual void func3() {
		cout << "A func3" << endl;
	}
};

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

	virtual void func5() {
		cout << "B func5" << endl;
	}

	virtual void func6() {
		cout << "B func6" << endl;
	}
};

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

在这里插入图片描述

基类虚函数在子类虚函数前面, 虚函数按照声明顺序放于表中

3. 继承(虚函数覆盖)

#include <iostream>

using namespace std;

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

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

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

	virtual void func1() {
		cout << "B func1" << endl;
	}

	virtual void func2() {
		cout << "B func3" << endl;
	}

	virtual void func7() {
		cout << "B func6" << endl;
	}
};

在这里插入图片描述
派生类的函数被放到原来基类虚函数的位置(覆盖)
虚函数表添加顺序还是先基类, 后按声明顺序添加派生类的虚函数

4. 多重继承(无虚基类, 无虚函数覆盖)

#include <iostream>

using namespace std;

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

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class AA {
public:
	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
};

class B : public A, public AA {
public:

	virtual void func6() {
		cout << "B func6" << endl;
	}

	virtual void func4() {
		cout << "B func4" << endl;
	}

	virtual void func5() {
		cout << "B func3" << endl;
	}

	virtual void func7() {
		cout << "B func6" << endl;
	}

};

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

在这里插入图片描述
派生类有两个虚函数表, 根据继承先后排列, 派生类的虚函数添加到第一个继承基类的虚函数表中

5. 多重继承(无虚基类, 有虚函数覆盖)

#include <iostream>

using namespace std;

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

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class AA {
public:
	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
};

class B : public A, public AA {
public:

	virtual void func1() {
		cout << "B func1" << endl;
	}

	virtual void func4() {
		cout << "B func4" << endl;
	}

	virtual void func5() {
		cout << "B func3" << endl;
	}

	virtual void func2_AA() {
		cout << "B func2" << endl;
	}

};

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

在这里插入图片描述
6. 多重继承(有虚基类, 无虚函数覆盖)

#include <iostream>

using namespace std;

class Base {
public:
	Base(int base):m_base(base){ cout << "Base地址:" << this << endl; }

	virtual void func10() {
		cout << "Base func10" << endl;
	}
public:
	int m_base;
};

class A: virtual public Base {
public:
	A(const Base& base, int a): Base(base), m_a(a) { cout << "A的地址:" << this << endl;}

	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
public:
	int m_a;
};

class AA: virtual public Base {
public:
	AA(const Base& base, int aa) : Base(base), m_aa(aa) { cout << "AA的地址:" << this << endl; }

	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
public:
	int m_aa;
};

class B : public A, public AA{
public:
	B(const Base& base, int a, int aa) : Base(base), A(base, a), AA(base, aa) { cout << "B的地址:" << this << endl; }
	
	virtual void func11() {
		cout << "B func11" << endl;
	}
public:
	int m_b;
};

int main(){
	B b(Base(2), 10, 11);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
多了vbptr和vbtable
A对应vbptr指针位置 + vbtable[0][0] = A对应vfptr的指针位置(4 + -4 = 0)
A对应vbptr指针位置 + vbtable[0][1] = Base对应vfptr的指针位置(4 + 24 = 0)

	int** t = (int**)(int*)(((int*)&b + 1) + 0); // 指向A的vbtable
	cout << t[0][0] << endl;					 // -4
	
	int** baseFun = (int**)((int*)&b + 7);  // 指向base的vbptr
	cout << baseFun[0][0] << endl;          // 7607512
	pFun = (Fun)(baseFun[0][0]);			// 指向Base::func10()函数的地址
	pFun();									// Base func10

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

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

相关文章

react相关概念

真实DOM和虚拟DOM区别 react关于虚拟DOM和真实DOM 虚拟DOM比较“轻”&#xff0c;真实DOM比较“重”&#xff0c;因为虚拟DOM是React在用&#xff0c;无需真实DOM上那么多属性 虚拟DOM最终一定会转为真实DOM放入页面 JSX JSX: 全称JavsScript XML 是react定义的一种类似于XM…

西门子S7-1200内部存储区和掉电数据保持设置

S7-1200内部存储区分类 S7-1200的内部存储区分为工作存储区、装载存储区和保持性存储区三种。 装载存储区 是非易失性存储区。用于存储用户项目文件&#xff08;用户程序、数据和组态&#xff09;。 如果不使用存储卡&#xff0c;用户使用TIA PORTAL软件下载项目即下载到CPU内…

jpg图片怎么压缩大小,3个有效工具分享

jpg图片怎么压缩大小&#xff1f;很多小伙伴都会用电脑来保存平时拍摄在的照片吧&#xff0c;拍摄的照片大多是jpg格式的&#xff0c;如果图片的体积太大&#xff0c;那么可能会占用很多的电脑存储空间&#xff0c;最后的结果是电脑变得非常慢&#xff0c;甚至卡死。平时我们在…

Python词云图的制作与案例分享

一、基本知识 Python 有很多可用于制作词云图的库&#xff0c;其中比较常用的有 wordcloud 和 jieba。 wordcloud 是一个用于生成词云图的 Python 库&#xff0c;其使用了 Python 的 PIL 库和 numpy 库。您可以使用 pip 命令来安装 wordcloud 库&#xff1a; pip install wo…

《计算机网络——自顶向下方法》精炼——2.6.2-2.7.1

“An investment in knowledge pays the best interest.” - Benjamin 文章目录 分布式散列表&#xff08;键-值&#xff09;对散列函数逻辑上的实现环形DHT对等方扰动对等方离开对等方加入 UDP套接字编程客户端代码服务器端代码 分布式散列表 分布式散列表是一种数据库。集中…

NIO基础 - 网络编程

non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream&#xff0c;它就是读写数据的双向通道&#xff0c;可以从 channel 将数据读入 buffer&#xff0c;也可以将 buffer 的数据写入 channel&#xff0c;而之前的 stream 要么是输入…

STM32配置ADC2(DMA)进行采集 DAC 输出-1

1.实验目标 在正点原子的ADC&#xff08;DMA&#xff09;例程上&#xff0c;将ADC1改成ADC2来采集电压&#xff0c;并且进行测试 开发板&#xff1a;正点原子探索者STM32F407ZG 2.查看adc.h和开发指南需要修改哪些配置 ADC相关 通过查看开发指南我们发现&#xff0c;ADC1 和…

垃圾分类算法

垃圾分类算法 垃圾分类算法系统整体结构Python环境TensorFlow环境微信小程序及后台服务器环境数据预处理 垃圾分类算法 基于TensorFlow和VGG-16卷积神经网络训练垃圾分类模型&#xff0c;通过服务器实现分类模型移植到移动端&#xff0c;并在微信小程序中进行应用。 系统整体…

【ROS仿真实战】机器人模型描述文件介绍(二)

文章目录 前言一、URDF与XACRO简介1.1 URDF1.2 XACRO 二、三维模型的选择DAE还是STL&#xff1f;三、 总结 前言 URDF和XACRO是ROS机器人领域中用于描述机器人模型的两种XML格式文件&#xff0c;都是使用XML语法来定义机器人模型的结构、物理特性以及运动学参数等。URDF (Unif…

还在用MySQL存日志?

1. 引言 随着智能硬件成本降低和互联网网络的发展&#xff0c;在生活中场景的设备都已接入了网络中&#xff0c;其中传感器成为了设备获取外界信息的眼睛&#xff0c;网络成为设备交流的桥梁。在设备的交互中&#xff0c;往往存在着触发源&#xff0c;即标识控制指令的来源&am…

大型水利投资集团,打造数智财资管理新范式

随着我国城市化进程的不断推进&#xff0c;城市基础设施在国民经济中的作用愈加重要&#xff0c;其建设水平直接影响一个城市的竞争力。国有城投、水投等企业作为城市基础设施建设的主要参与者&#xff0c;其重要性不言而喻。随着国家、地方对基础设施重要性认识的加深以及政府…

第十章 游戏对象和组件访问

本节我们主要学习GameObject类&#xff0c;该类用于表示任何存在于场景中的游戏对象。这个类继承自Unity Object类&#xff08;不是C#语言的Object类&#xff09;&#xff0c;我们可以理解这个Unity Object类是所有Unity的基类。这个Unity Object基类比较简单&#xff0c;我们很…

啃完这份笔记,18K妥妥的了......

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;得准备面试了&#xff0c;又不知道从何下手&#xff01;为了帮大家节约时间&#xff0c;特意准备了一份面试相关的资料&#xff0c;内容非常的全面&#xff0c;真的可以好好补一补&#xff0c;希望大家在都能拿到理想…

MATLAB | 如何使用MATLAB绘制高度自定义的桑基图(sankey)

我之前也出过一个超简单的桑基图绘制函数&#xff0c;但是无法应对很多特殊情况&#xff0c;在这里我将其重构了一些写成了类&#xff0c;加了很多内置修饰函数&#xff0c;实现了流入流出数据不相等或者跨层数据流动的特殊情况绘制&#xff0c;首先展示一下使用我编写的函数能…

基于DSP+FPGA+ADS1282支持32Bit高精度数据采集方案(二)模拟电路设计

如图 4.1 所示是系统硬件系统的信号框图&#xff0c;数字信号处理板上的主要核心是 两个处理芯片&#xff0c;即 FPGA 和 DSP &#xff0c;其中 FPGA 主要作用是做 DSP 和外围接口的 桥梁及数据预处理&#xff0c; DSP 做为数据解算核心。 FPGA 通过各种的数据总…

【C++初阶】类和对象(一)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【C初阶】…

一文看懂低代码,5分钟从入门到原理全搞定

全球低代码市场已经走过了近20年&#xff0c;中国低代码市场近5年经历了百花齐放的广泛探索阶段&#xff0c;更旺盛的市场需求逐步在被激发。现在&#xff0c;让我们按下暂停键&#xff0c;看看这些产品给我们呈现了低代码市场一幅怎样的百景图。 低代码平台简介 广义上的低代…

[nesbot/carbon]轻松优雅的驾驭时间处理

简介 这个库的名字其实就很有意思&#xff1a;“carbon”&#xff0c;是化学元素的名字“碳”&#xff0c;为什么叫这个名字呢&#xff1f;在科学界&#xff0c;有一个"放射性碳定年法"的东西&#xff0c;是一种利用碳的同位素14C的放射性来对含有有机物质的物品进行…

废物,我TMD一个985却斗不过专科生(大厂自动化测试2年被裁)

前言 看到标题&#xff0c;可能很多读者朋友恐怕又要骂我了&#xff0c;985这个特殊的字眼也确实异常晃眼&#xff0c;实际上现在985&#xff0c;211也越来越多&#xff0c;它能代表你能够进入到更高的平台&#xff0c;拿到“高级工厂”的入场券&#xff0c;但并不意味着你会成…

每天一道算法练习题--Day14 第一章 --算法专题 --- -----------大话搜索

大话搜索 搜索一般指在有限的状态空间中进行枚举&#xff0c;通过穷尽所有的可能来找到符合条件的解或者解的个数。根据搜索方式的不同&#xff0c;搜索算法可以分为 DFS&#xff0c;BFS&#xff0c;A*算法等。这里只介绍 DFS 和 BFS&#xff0c;以及发生在 DFS 上一种技巧-回…