【VC++】虚函数 内存结构 - 第二篇(单继承,无虚函数覆盖)

C++内存分布 虚表 虚指针(非常重要) C++内存分布 虚表 虚指针: class Base { public: int m_base; }; class DerivedA: public Base { public: int m_derivedA; }; class DerivedB: public Base { public: int m_derivedB; }; class D 阅读详情

【VC++】虚函数 内存结构 - 第二篇(单继承,无虚函数覆盖)

#include <IOSTREAM>
using namespace std;

class Base
{
public:
	int nBase1;
	int nBase2;
	
	Base(int n1,int n2):nBase1(n1),nBase2(n2)
	{
		cout<<"Base::Base("<<n1<<","<<n2<<")"<<endl;
	}
	
	virtual void F()
	{
		cout<<"Base::F()"<<endl;
	}
	virtual void G()
	{
		cout<<"Base::G()"<<endl;
	}
};

class Derive:public Base
{
public:
	int nDerive1;
	int nDerive2;
	
	Derive(int n1,int n2,int n3,int n4):Base(n1,n2),nDerive1(n3),nDerive2(n4)
	{
		cout<<"Derive::Derive("<<n3<<","<<n4<<")"<<endl;
	}
	
	virtual void F2()
	{
		cout<<"Derive::F2()"<<endl;
	}
	virtual void G2()
	{
		cout<<"Derive::G2()"<<endl;
	}
};


typedef void(*Fun)(void);

void TestBase(Base &b)
{
	Fun pFun = NULL;
	int** pVtab = (int**)&b;
	
	cout << "类对象地址 / 虚函数表指针的地址:" << (&b) << " / " << pVtab << endl;
	cout << "虚函数表指针值(虚函数表的地址)(十进制):" << (int)pVtab[0] <<endl;
	cout << "虚函数表指针值(虚函数表的地址)(十六进制):" << pVtab[0] <<endl; 
	
	cout << "虚函数表 — 第 1 个函数地址的地址:"  <<  &pVtab[0][0] << " , ";	//好比一个变量的地址
	cout << "函数地址值:"  << (int*)pVtab[0][0] << " 调:";	//好比一个变量的值(这个值也是个地址)
	pFun = (Fun)pVtab[0][0];
	pFun();
	
	cout << "虚函数表 — 第 2 个函数地址的地址:"  <<  &pVtab[0][1] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][1] << " 调:";
	pFun = (Fun)pVtab[0][1];
	pFun();
	
	cout << "虚函数表 — 第 3 个函数地址的地址:"  <<  &pVtab[0][2] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][2] <<endl ;	//如果仅有一个虚函数,后面的节点不是NULL???
	//pFun = (Fun)pVtab[0][2];
	//pFun();
	
	cout << "类对象 — 第 1 个变量地址:" << &pVtab[1]  << " 值:"<< (int)pVtab[1] <<endl;	
	cout << "类对象 — 第 2 个变量地址:" << &pVtab[2]  << " 值:"<< (int)pVtab[2] <<endl;
	cout << "类对象 — 第 3 个变量地址:" << &pVtab[3]  << " 值:"<< (int)pVtab[3] <<endl;	//之后的内存是什么???
	
	cout<<endl;
}

void TestDerive(Derive &d)
{
	Fun pFun = NULL;
	int** pVtab = (int**)&d;
	
	cout << "类对象地址 / 虚函数表指针的地址:" << (&d) << " / " << pVtab << endl;
	cout << "虚函数表指针值(虚函数表的地址)(十进制):" << (int)pVtab[0] <<endl;
	cout << "虚函数表指针值(虚函数表的地址)(十六进制):" << pVtab[0] <<endl; 
	
	cout << "虚函数表 — 第 1 个函数地址的地址:"  <<  &pVtab[0][0] << " , ";	//好比一个变量的地址
	cout << "函数地址值:"  << (int*)pVtab[0][0] << " 调:";	//好比一个变量的值(这个值也是个地址)
	pFun = (Fun)pVtab[0][0];
	pFun();
	
	cout << "虚函数表 — 第 2 个函数地址的地址:"  <<  &pVtab[0][1] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][1] << " 调:";
	pFun = (Fun)pVtab[0][1];
	pFun();

	cout << "虚函数表 — 第 3 个函数地址的地址:"  <<  &pVtab[0][2] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][2] << " 调:";
	pFun = (Fun)pVtab[0][2];
	pFun();

	cout << "虚函数表 — 第 4 个函数地址的地址:"  <<  &pVtab[0][3] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][3] << " 调:";
	pFun = (Fun)pVtab[0][3];
	pFun();
	
	cout << "虚函数表 — 第 5 个函数地址的地址:"  <<  &pVtab[0][4] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][4] <<endl ;	//如果仅有一个虚函数,后面的节点不是NULL???

	
	cout << "类对象 — 第 1 个变量地址:" << &pVtab[1]  << " 值:"<< (int)pVtab[1] <<endl;	
	cout << "类对象 — 第 2 个变量地址:" << &pVtab[2]  << " 值:"<< (int)pVtab[2] <<endl;
	cout << "类对象 — 第 3 个变量地址:" << &pVtab[3]  << " 值:"<< (int)pVtab[3] <<endl;
	cout << "类对象 — 第 4 个变量地址:" << &pVtab[4]  << " 值:"<< (int)pVtab[4] <<endl;
	cout << "类对象 — 第 5 个变量地址:" << &pVtab[5]  << " 值:"<< (int)pVtab[5] <<endl;	//之后的内存是什么???
	
	cout<<endl;
}

void main()
{
	Base b(111,222);
	Derive d(123,321,456,654);
	Derive d2(1,2,3,4);

	TestBase(b);
	TestDerive(d);
	TestDerive(d2);

	cout<<endl;
}

运行结果:
在这里插入图片描述

父类对象、子类对象,各种地址和值的比对(红色表示不同的地方):

在这里插入图片描述
同一子类,两个不同对象 的比对:

在这里插入图片描述

类图、监视值(可以看到,VS的监视值并不全面,还是以实际打印的为准):
在这里插入图片描述
在内存中的结构是:
在这里插入图片描述
在本文结束前,我也还是来介绍下自己吧。我从03年初学编程,一直到现在都在做软件开发方面的事,做过VC++、VB/Delphi/C++Builder、C#/Asp.Net、Java、Web前后端、数据库、服务器端、客户端、js、lua、Flash AS、cocos2dx/Unity3d……都有接触过,甚至做过很长一段时间的运营、产品策划、美工、客服、线下推广员、公司经理 多职务……技术只是一种工具,用来实现业务需求,所以最大的特长是逻辑思维、挖掘业务需求、强化用户体验、快速的产品实现,最自以为豪的,我做过的很多产品,很稳定,bug极少(当然也出现过没考虑周全的漏洞在所难免但总体上也还算少),有很多独立开发的游戏运营到现在还在稳定的运行。不是技术极客,而是业务需求带动技术的学习研究,所以也不怎么挑语言,编程语言大体上也都是相通的,底层原理也有很多类同的地方。所以缺点从不敢说精通哪一门语言。遇到小工具、小需求的订制实现,我个人最喜欢的还是用VC6,简洁干净,编译出来的东西 体积小,速度快,占CPU内存小。当然实际工作中,还是随项目。欢迎大家和我交流,我的QQ是:七6.肆-陆_柒-4`7_四.
生活中的我,热爱 街舞、飙车、花式游泳、游戏一条命通关……极限运动,我抖音号(一不小心玩出花样)是:1917940952

文章没有考虑字节对齐(都用默认的字节对齐),并且是在32位环境下,VC6、VS2017 都测试过。

	cout<<"sizeof(Base): "<<sizeof(Base)<<endl;
	cout<<"sizeof(Derive): "<<sizeof(Derive)<<endl;

在这里插入图片描述

题外扩展:可以禁止生成虚表。我们可以通过 __declspec(novtable) 来告诉编译器不要生成虚表,ATL中大量应用这种技术来减小虚表的内存开销,比如将本文的Base类改成:
class __declspec(novtable) Base
那么在调用 TestBase(b) 时会出现错误:
在这里插入图片描述
所以,需要把 TestBase(b) 注释掉后 //TestBase(b); 进行sizeof(Base)发现仍然是12,监视到类对象b的虚表指针仍在,但虚函数表已经不在了:
在这里插入图片描述

结论: 通过__declspec(novtable),我们只能禁止编译器生成虚表,但是不能阻止对象仍包含虚表指针(不能减小对象的大小),也不能阻止程序对虚表的访问(尽管实际虚表不存在),所以禁止生成虚表只适用于永远不会实例化的类(基类)。

(转载时请注明作者和出处。未经许可,请勿用于商业用途)
原创出处:https://blog.youkuaiyun.com/maoyeahcom/article/details/108771096

C/C++面试题2「虚函数、多态、内存管理与软件调试篇」 C/C++面试题分享「虚函数、多态、内存管理与软件调试篇」 阅读详情

相关推荐

基于PINN的一个简单的求解偏微分方程实例

本人是做化工模拟的一名学生,最近在学习PINN(Physics-Informed Neural Networks,物理信息神经网络)对管道动力学进行建模研究,PINN由于其对机理与数据的良好兼顾性非常热门,本文是作者对PINN入门研究过程的一个练手小题目,main code也是作者代码是基于pytorch2.6.0写的,发上来供各位参考,欢迎研究管道动力学和PINN的小伙伴一起讨论~

weixin_64491760的博客 1741

虚函数打印-单继承无函数覆盖

虚函数打印-单继承无函数覆盖 struct func1 { public: virtual void Box1() { printf("Box1\n"); } virtual void Box2() { printf("Box2\n"); } }; struct func2 : func1 { public: virtual void Box3() { printf("Box3\n"); } virtual void Box4() { printf("Box4\n");

weixin_42408832的博客 203

ROS TF坐标转换实战:从零搭建机器人坐标系(附常见问题排查)

本文提供了一份ROS TF坐标转换的实战指南,详细讲解了如何从零为机器人搭建完整的坐标系,并深入解析了TF的原理。内容涵盖静态与动态坐标变换的发布、URDF模型使用、以及Rviz和rqt_tf_tree等可视化调试工具。文章还附带了常见问题排查表,帮助开发者快速定位并解决TF树断裂、数据错位等典型问题。

weixin_29268385的博客 204

C++ 虚函数表原理和类的内存分布

C++ 虚函数表原理和类的内存分布

恋恋风辰的技术博客 1935

【星火】海思HI3516C开发板实战:基于LVGL与MPP库的行车记录仪/电子显微镜方案

本文详细介绍了基于海思HI3516CV500开发板,结合LVGL图形库与海思MPP媒体处理平台,实现行车记录仪与电子显微镜方案的完整实战过程。内容涵盖从系统环境搭建、驱动配置到应用开发的全流程,重点解析了MPP视频流处理管道与LVGUI界面协同工作的核心机制,为嵌入式音视频开发提供了可复用的参考方案。

weixin_42126677的博客 162

电子技术设计——16×16点阵汉字显示设计

同时,设计还应注重提升显示品质,以保证汉字的稳定展示和观感舒适。本设计的目的是构建一个能够准确在LED点阵上展示汉字的系统,并允许用户通过输入设备来选择所需的汉字,我们将验证设计方案的实际操作性和效果,以确保其满足设计目标。在LED点阵显示和汉字信息处理技术的结合下,研究者们着手研究如何在LED点阵上有效地显示汉字,并提出了一系列显示算法和优化策略,旨在实现清晰且易于阅读的汉字显示效果。首先,你需要准备16×16的LED点阵面板,通常这些点阵是8×8点阵的两倍,因此可以由两个8×8的点阵组合而成。

weixin_68094467的博客 1533

虚函数的内存结构

虚函数的内存结构 虚函数的内存结构 一、 基本概念     C++中的虚函数的作用主要是实现了多态的机制。     关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。     所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RT

那些年 1199

【NLP】使用 PyTorch 从头构建自己的大型语言模型 (LLM)

如果我们在 n 维空间中绘制一个向量,看起来相似的物体(如狗和猫)将彼此靠近,而看起来不相似的物体(如学校和家庭)的嵌入向量将位于更远的地方。为此,我们将使用一种流行的标记器,称为 BPE 标记器,这是一种子词标记器,正在 GPT3 等模型中使用。:前馈网络使用深度神经网络来学习两个线性层(第一层有 d_model 节点,第二层有 d_ff 节点,根据注意力论文分配值)中嵌入向量的所有特征,并且将 ReLU 激活函数应用于第一线性层的输出,为嵌入值提供非线性,并应用 dropout 以进一步避免过度拟合。

sikh_0529的博客 1951

虚函数内存布局解析

主要在于功能解耦合以及规范化的需要。我们很多时候需要做到:**一个模块对达成某个功能(机制)有一组可替换的实现(策略)。也就是说,需要策略和机制分离。那么我们的模块就应该只依赖这个机制的接口,而非这个机制的具体实现。,这是怎么实现的呢?中更加完善,它会告诉妳这是一个接口。,这里如果我们想在业务中使用。都生成了非虚的析构函数,直接。的功能,那么我们就必须把。可能导致释放不完全。

ranxirui8580的博客 399

解锁全志T113-i隐藏技能:用RISC-V核跑实时系统比STM32还稳?

本文深入探讨了全志T113-i SoC的异构架构潜力,重点解析如何利用其内置的玄铁C906 RISC-V核心独立运行实时系统。通过对比传统MCU与Linux方案,文章提供了从RISC-V核心启动、FreeRTOS移植到与Linux主核进行高效异构通信的完整实战指南,为工控等需要兼顾丰富应用与硬实时控制的场景提供了高集成度的单芯片解决方案。

ansible6ops的博客 494

微信小程序逆向实战:从HTTPS抓包到API接口全解构

方案原理优点缺点方案一:使用低版本微信+系统代理微信7.0以下版本未强制不走代理操作简单容易被封号,无法使用新版小程序方案二:Hook SSL层(Xposed/Frida)动态Hook Java的TrustManager或系统的SSLContext,绕过证书校验灵活可控需要Root/越狱,技术门槛高方案三:VPN转发抓包(PacketCapture)利用Android VPNService将所有流量转发到本地代理无需Root,兼容性好部分机型存在性能问题。

2201_76125261的博客 267

虚函数表内存布局全解析,洞悉C++多态本质

深入解析c++多态的实现原理虚函数表,揭示对象内存布局与动态调用机制。涵盖继承体系下虚函数表结构、多态适用场景及性能优势,助你掌握底层原理,提升程序设计能力,值得收藏。

FuncWander的博客 209

MATLAB - 激光雷达 - 相机联合标定(Lidar-Camera Calibration)

激光雷达传感器和相机通常在自动驾驶应用中结合使用,因为激光雷达传感器收集三维空间信息,而相机则以二维图像捕捉空间的外观和纹理。您可以融合来自这些传感器的数据来改进物体检测和分类。激光雷达-相机标定可以估算出一个变换矩阵,给出两个传感器之间的相对旋转和平移。在进行激光雷达-相机数据融合时,您可以使用该矩阵。本图说明了激光雷达和相机标定 (LCC) 过程的工作流程,我们使用棋盘格作为标定对象。我们从激光雷达和相机数据中提取棋盘角和平面,然后在它们的坐标系之间建立几何关系,进行标定。

机器人控制与仿真应用 7371

【VC++】虚函数 内存结构 - 第三篇(单继承,有虚函数覆盖)

【VC++】虚函数 内存结构 - 第三篇(单继承,有虚函数覆盖) #include <IOSTREAM> using namespace std; class Base { public: int nBase1; int nBase2; Base(int n1,int n2):nBase1(n1),nBase2(n2) { cout<<"Base::Base("<<n1<<","<<n2<<")"<<end

maoyeahcom的博客 222

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)

【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)

maoyeahcom的博客 266

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【VC++】虚函数 内存结构 - 第五篇(多重继承,有虚函数覆盖)

【VC++】虚函数 内存结构 - 第五篇(多重继承,有虚函数覆盖)

maoyeahcom的博客 254

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Vc++内存布局

Vc++内存布局 测试平台 Windows server 2012 R2 and visual studio 2013 professional。 本篇文章意在介绍vc++中类的内存布局方式,只是研究其大概的排列方式,对于内存对齐方面则没有进行深入探讨。如有需要研究类的具体大小可以参考其他博客。 测试方式 在vs中,查看类的内存布局主要有两种方式。一种是实例化一个类,然后在de...

antangzhang9759的博客 197

【VC++】虚函数 内存结构 - 第一篇(单类)

前言 - 为何写此系列文章? 网上讨论虚函数内存结构的文章很少,好不容易有几位大神写了几篇很精彩的文章,结果里面还有错误: 陈皓大神的《C++ 虚函数表解析》: https://blog.youkuaiyun.com/haoel/article/details/1948051 《C++ 对象的内存布局(上)》: https://blog.youkuaiyun.com/haoel/article/details/3081328 《C++ 对象的内存布局(下)》: https://blog.youkuaiyun.com/haoel/article

maoyeahcom的博客 350

VC_VC++类继承内存布局

上看到了一篇写得非常好的文章,是有关c++类继承内存布局的。看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用。 ——谈VC++对象模型 (美)简.格雷 程化    译 译者前言 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。对于使用VC++的程序员来说,还应该了解一些VC++对于C++的诠释。 Inside the C++ Object

优快云-MarkYang 2541

Windows软件开发实践:C++虚函数与虚函数表

摘要:本文深入探讨了C++虚函数表(vtbl)的实现机制及其在继承中的特性。虚函数表是存储虚函数地址的指针数组,位于类对象内存起始处,所有对象共享同一虚表。文章通过代码实例验证了虚表的存在和访问方式,并分析了5种继承场景下的虚表布局:(1)基类、(2)单继承无覆盖、(3)单继承有覆盖、(4)多重继承无覆盖、(5)多重继承有覆盖。实验表明,派生类会继承基类虚表,重写虚函数会替换对应表项,多重继承会产生多个虚表。研究揭示了C++多态性的底层实现原理。

fairyfar的专栏 583

再谈带有虚函数的类对象之内存结构

前篇:http://blog.youkuaiyun.com/pathuang68/archive/2009/04/21/4096521.aspx 下面的代码中,类Base中定义了两个虚函数vfBase_1()和vfBase_2(),另外还定义了一个整形成员变量m_base;#include using namespace std; class Base{public:   

玄机逸士的专栏 1729

C++基础 虚函数

析构函数需要为虚函数, 默认静态联编, delete会调用指针类型的析构函数, 释放派生类对象中基类指向的内存, 不会释放派生类指向的内存。析构函数时虚的, 则会先调用对象析构函数先释放派生类指向的内存, 再调用基类析构函数释放基类指向的内存。友元不能是虚函数, 友元不是类成员, 只有成员才能是虚函数, 可以用过友元函数使用虚成员函数来解决。派生类有两个虚函数表, 根据继承先后排列, 派生类的虚函数添加到第一个继承基类的虚函数表中。会发现同一类的对象用的虚函数是相同的, 都指向一个指针。

zbbzb的博客 1397

[转载]C++继承与内存分配,虚基类指针vbptr和虚函数指针vfptr

转载:继承 内存分布 原作者:DylanWind class Base { public: int m_base; }; class DerivedA: public Base { public: int m_derivedA; }; class DerivedB: public Base { public: int m_deriv...

JMcc_的博客 887

C++多态底层机制:从虚函数表到内存布局的完整解析

面向对象编程中的多态性是实现代码复用和扩展性的核心技术,它允许程序在运行时根据对象的实际类型调用相应的方法。其核心原理是通过虚函数表实现动态绑定,编译器为每个包含虚函数的类生成一个函数指针数组,对象通过虚函数表指针间接调用正确的函数实现。这一机制虽然引入轻微的性能开销,但为软件设计带来了极大的灵活性,广泛应用于框架设计、插件系统和算法策略等场景。本文深入剖析C++中虚函数表的内存布局和汇编级调用过程,并探讨虚析构函数的必要性,帮助开发者从底层理解多态的实现机制。

weixin_30664615的博客 402

【C++ 篇 7】类的大小由什么决定?内存对齐、虚函数和纯虚函数的区别

内存对齐就是为了让内存存取更加有效率而采取的一种优化手段,对齐的结构是使得内存中数据的首地址是CPU单次获取数据大小的整数倍。

2401_85061293的博客 1030

c++继承中的内存布局

 今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的。看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用。 ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。对于使用VC++的程序员来说,还应该了解一些VC++对于C++的诠释。 Inside the C++ Object

cesc 1万+
上一篇: 【VC++】虚函数 内存结构 - 第一篇(单类)
下一篇: 【VC++】虚函数 内存结构 - 第三篇(单继承,有虚函数覆盖)
maoyeahcom
博客等级 码龄15年 14粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值