(多继承+模板回传子类)的结构所遇到的问题

关于filesystem在c++11、14、17中的使用问题 1在C++11时,该类库定义在std::tr2::sys命名空间 #include<filesystem> using namespace std; using namespace std::tr2::sys; //示例 void RemoveFile() { directory_iterator end, ite("aaa\\"); for (; ite != end; ++ite) { auto& thePath = ite-&gt. 阅读详情

(一)问题初现

(1)问题代码

在调试一个项目时,发生崩溃,通过调用堆栈看是访问成员变量时发生的,代码结构如下

class BaseA
{
public:
BaseA() { cout << "new BaseA " << endl; }
virtual ~BaseA() {};
int m_aa;
};

template< typename T>
class BaseB
{
public:
BaseB(){cout << "new BasBe " << endl;}
virtual ~BaseB(){}

void Test()
{
Func(this);
}
private:
void Func(void* pObj)
{
T* pThis = (T*)(pObj);
pThis->Run();
}
int m_bb;
};

class Drive:  public BaseA, public BaseB<Drive>
{
public:
Drive()
{
m_cc = 5;
cout << "new Drive " << endl;
}
virtual ~Drive(){ }

void Run(bool bl=true)
{
cout << "m_cc = " << m_cc << endl;
}

int m_cc;
};

(2)调用方法

Drive* pDrive = new Drive();
pDrive->Test();

(二)问题研究

(1)尝试解决

通过尝试,发现几种方法都可以使代码结果正确

方法1) 调换Drive的继承顺序

class Drive:  public BaseB<Drive>,public BaseA

方法2)  去掉BaseA的虚析构函数

//virtual ~BaseA() {};

方法3)直接调用Func的代码

void Test()
{
T* pThis = (T*)(this);
pThis->Run();
}

(2)问题探究

虽然上面能解决问题了,但是没有了解其本质,上面三种方法都是不同的门,
最后通向的是同一个房间,我应该找到那个房间,才是解决问题的本质。
推断问题原因与C++多态机制有关,于是通过vs工具查看了Drive实例的内存布局
在vs命令行的添加命令:/d1reportSingleClassLayoutDrive Test.cpp 
编译时可看到输出的Drive类型的内存结构信息:
class eLane::Drive size(20):
1> +---
1> 0 | +--- (base class BaseA)
1> 0 | | {vfptr}
1> 4 | | m_aa
1> | +---
1> 8 | +--- (base class BaseB<class Drive>)
1> 8 | | {vfptr}
1>12 | | m_bb
1> | +---
1>16 | m_cc
1> +---

这里有两个知识点:

1) 多态类对象的内存布局

如果基类有虚函数,派生类对象内存中,会将该基类虚表指针及成员数据放到头部
如果是多继承,顺序以派生类定义时的顺序为准
没有虚函数的基类不会放在头部

2)基类与派生类的指针转换

派生类内存地址转为基类地址时,编译器会做对应的偏移处理,使其指向正确的内存起始地址通过上面的结构信息可以看到,最前面是BaseA的虚表指针和成员变量m_aa
接着是BaseB<class Drive>的虚表指针和成员变量m_bb
最后是Drive的成员变量m_cc

(3)问题明了

Drive* pDrive = new Drive(),pDrive指向完整的Drive内存的首地址
pDrive->Test()中的Func(this),this实际是指向Drive内存中BaseB的起始地址,编译器做了偏移
通过转为Func的void*参数传入其内部后执行 T* pThis = (T*)(pObj)
由于pObj的类型不明确,编译器无法还原为派生类型地址,所以pThis指向的仍是BaseB的起始地
再执行pThis->Run(),调用Drive的成员函数是无法访问到正确成员变量,
其实问题本质就是在一个BaseB的内存实例上,调用了Drive的成员函数,导致错误的内存访问。

(4)不同方法的相同原理

1)调换Drive的继承顺序:class Drive:  public BaseB<Drive>,public BaseA
Drive内存中最前面放置的是BaseB类,这样BaseB的起始地址与Drive的起始地址相同
两者指针在转换时偏移为0,所以即使错用也不会出错

2)注释掉BaseA的虚析构函数://virtual ~BaseA() {};
Drive内存最前面原来是BaseA,当BaseA没有虚函数时,不需要虚表实现多态
BaseA的成员数据放到了Drive内存的后面,这样BaseB到了最前面,效果同 1)

3)Test()中直接调用Func的代码
T* pThis = (T*)(this),这里this的类型是明确的BaseB,编译器能够做正确偏移
使得pThis指向Drive的起始地址,可正常调用Run

(5)更多的方法

在Func中使用以下两种方法也是有效的
1)告诉编译器内存地址真实类型

void Func(void* pObj)
{
T* pThis = (T*)((Base* )pObj);
pThis->Run();
}


2)避免使用void*

void Func()
{
T* pThis = (T*)(this);
pThis->Run();
}

2026年小米嵌入式岗位面试题带详细答案 小米嵌入式开发面试核心考点摘要 本文针对小米嵌入式开发岗位面试整理47道高频考题,覆盖硬件外设驱动、MCU开发、Linux驱动、操作系统等核心模块。重点内容包括: 硬件外设(7题):详解SPI/I2C/UART通信协议差异、GPIO配置模式、ADC采样优化及中断处理原则。 MCU开发(3题):对比STM32F103与F429特性差异,分析裸机开发中内存碎片和环形缓冲区的解决方案。 系统启动(3题):解析U-Boot两阶段启动流程,涵盖硬件初始化到操作系统加载的全过程。 Linux驱动(5题):探讨驱动模型架 阅读详情

相关推荐

2026年荣耀嵌入式岗位高频面试题(含参考答案)

本文总结了荣耀嵌入式岗位面试的核心考察内容,涵盖硬件外设、RTOS/Linux系统、底层编程等7大模块共36道高频考题。硬件部分重点解析I2C通信流程、串行协议对比及蓝牙开发流程;RTOS模块分析FreeRTOS与Linux的区别及多任务调度机制;操作系统基础则对比进程与线程的核心差异。题目设置贴合实际岗位需求,兼顾校招与社招场景,帮助应聘者系统掌握嵌入式开发的关键知识点与工程实践能力。

Open 53

flutter学习笔记

1.跨平台移动UI框架2.与现有的代码一起工作(可以直接嵌入到原生代码中或直接把原生代码嵌入到自己的代码中运行)3. 完全免费、开源。

haoxuhong的博客 8246

从零到一:用AXI DataMover在Zynq MPSoC上实现高效DMA传输(附Vitis HLS驱动示例)

本文详细介绍了如何在Xilinx Zynq UltraScale+ MPSoC平台上使用AXI DataMover实现高效的DMA数据传输。通过解析AXI DataMover架构、Vivado工程配置、Vitis HLS驱动开发及性能优化技巧,帮助开发者快速构建高性能数据传输通道,并提供了可直接复用的代码示例。

weixin_42520796的博客 426

C++使用localtime函数需要注意的地方

在处理时间时用到了localtime这个函数,将一个UTC时间转为本地时间: #include time_t tmNow = time(NULL); tm *ptmNow = localtime(&tmNow); //从tm结构体中可以取到年月日时分秒等值。 这里localtime返回了一个tm指针,空间是由localtime自己控制的,所以如果连续调用这个函数会有问题。 很多时

shellching的专栏 4万+

别再用Excel硬算传热了!用MATLAB/Octave复现国赛数学建模A题(附完整代码与避坑指南)

本文详细介绍了如何利用MATLAB/Octave高效解决数学建模中的传热问题,替代传统的Excel计算方法。通过复现2018年国赛A题的高温作业服传热模型,从理论推导到代码实现,提供了完整的有限差分法解决方案和参数拟合技巧,帮助读者提升数学建模效率和准确性。

weixin_30512965的博客 380

Playwright实战:打造绕过全维度前端反爬的无头浏览器爬虫(2026实战版)

Playwright绕过前端反爬的核心是模拟真实:真实的设备指纹、真实的行为轨迹、真实的网络请求;2025年前端反爬的核心检测维度是设备指纹+行为轨迹+网络请求,三者缺一不可;实战中需重点关注WebGL/Canvas指纹模拟、行为随机性、请求头完整性,这是绕过反爬的关键;性能优化需以“不触发反爬”为前提,优先选择浏览器复用、资源拦截,而非无限制并发。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2094

多字节与UTF-8、Unicode之间的转换

// 多字节编码转为UTF8编码bool MBToUTF8(vector& pu8, const char* pmb, int32 mLen){ // convert an MBCS string to widechar int32 nLen = MultiByteToWideChar(CP_ACP, 0, pmb, mLen, NULL, 0); WCHAR* lps

shellching的专栏 1万+

Android Framework 开发工程师职位深度解析与面试指南

摘要:博泰车联网科技(上海)股份有限公司招聘Android Framework开发工程师,负责车载系统的底层开发与优化。岗位要求5年以上经验,深入掌握Android Framework机制(WindowManager、PowerManager等模块),熟悉高通平台中间件架构及NDK/JNI开发,具备性能优化(启动时间、ANR处理)、OTA升级等能力。需熟练使用Android Studio、Git等工具,有车载开发经验者优先。工作内容包括Framework定制开发、问题排查及技术文档编写。

郑伟强dev的专栏 552

关于scanf、sscanf和sscanf_s

在使用VS2005编译一个程序时,出现了很多警告,说是用的函数是不安全的,应当使用安全版本,即函数名称增加“_s”的版本。  警告内容: warning C4996: sscanf: This function or variable may be unsafe. Consider using sscanf_s instead.  据了解,“_s”版本函数是微软后来对c++做得扩展,用来替代原

shellching的专栏 1万+

基于Transformer模型实现交通流量时序预测(PyTorch版)

Transformer模型,作为一种基于自注意力机制的深度学习模型,最初在自然语言处理领域取得了突破性进展,其出色的长距离依赖关系捕捉能力使其在处理时序数据时也表现出色。将Transformer模型应用于交通流量时序预测,可以充分利用其优势,实现对交通流量更加精准、高效的预测。

时序笔记 2572

华为OD机试真题2026双机位C卷 C语言 实现【明日之星选举】

给定一组选票votes。votes[i]代表第 i 张选票的内容,包含一个字符串"zhangsan,hanmei",表示推荐zhangsan为明日之星,反对 hanmei 称为明日之星。赞成或反对使用逗号(,)分割。 给定一个数组n,表示最后将筛选出赞成票最多的n个人,如果赞成票相同,则反对票越少,排序越靠前;如果赞成票和反对票都相同,则按照姓名字典序升序排序。

MISAYAONE的博客 184

初探ifstream与回车换行及类型匹配等几个问题

#include #includeusing namespace std;/*本例: 1、练习ifstream、ofstream读写文件基本方法; 2、验证回车换行符号在文件读写中的状态和作用; 3、验证几种判断文件末尾的方法; 4、验证几种解决ifstream对读入类型不匹配数据的处理方法回车与换行符: 字符 名称

shellching的专栏 9333

【Python机器学习】决策树与随机森林:解释性与鲁棒性的平衡

本文深入探讨了决策树和随机森林在解释性与鲁棒性之间的权衡。决策树以其完美的可解释性、快速的推理速度和无需特征标准化等优势,在许多场景中仍然是首选算法。一个经过合理剪枝的决策树,其每个决策路径都可以转化为业务规则,这在金融风控、医疗诊断等对透明度有严格要求的领域无可替代。然而,单棵决策树的高方差和容易过拟合的特性限制了其泛化能力。随机森林通过集成学习的思想,巧妙地解决了这个矛盾。它维持了相对较好的解释性(通过特征重要性、OOB估计等方式),同时通过多棵树的投票或平均,大幅降低了模型的方差。

nmdbbzcl的博客 627

关于Loadlibrary 失败-找不到指定模块126错误

近期C++做了一个简单的小功能放到动态库,主程序Loadlibrary动态加载,在本机调试好好的,发布到其他环境就出现加载失败问题,明明dll就在同目录下。 尝试了固定路径等都没有解决问题,主程序加载dll的路径经调试输出,确定没有问题。 这个时候最大的可能就是排查dll是否同时又引用了其他的dll,若有缺失那必然导致当前dll无法被加载。因为这个dll是vs2012编译的,所以环境必须有msvcp110.dll和msvcr110.dll两个基础库,可以安装2012的vcredist,也可以将两个dll

shellching的专栏 7001

【华为OD技术面试手撕真题】1、长度最小的子数组 | 手撕真题+思路参考+代码解析(C & C++ & Java & Python & JS)(三种解法)

【华为OD面试手撕真题】长度最小的子数组,本专栏更新每年华为OD机试的高频手撕代码题,每个题目都会使用五种语言进行解答(C & C++& Java & Python & JS),思路分析都非常详细, 实现最低的时间复杂度和高通过率,每个解题思路超过百字,内含各个编程语言的八股文面试题,欢迎大家订阅学习, 代码可以直接运行使用,全网限时最低价,持续更新

KJ.JK 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

c_c++控制台程序字符输入总结

while (1) { //标准c函数,包含于stdio.h //getchar() 字符,阻塞,要按回车,回显 //gets() 字串,阻塞,要按回车,回显 //微软c函数,包含于conio.h //getch() 字符,阻塞,不用按回车,不回显 //getche() 字符,阻塞,不

shellching的专栏 6589

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

vector、map容器删除操作总结

  1、关于vector中元素的删除和迭代器失效问题vector vv;vv.push_back(1); //加入第一个元素vector::iterator itBegin = vv.begin(); //获取第一个元素迭代器*itBegin=1vv.push_back(2); //因为预留不够,所以发生内存搬移,之前迭代器将全部失效vv.pus

shellching的专栏 6237

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

cout 重定向到文件

//打开输出文件 ofstream outf("out.txt"); //获取cout默认输出 streambuf *default_buf=cout.rdbuf(); //重定向cout输出到文件 cout.rdbuf( outf.rdbuf() ); //输出到文件 cout<<"输出到文件"<<endl; //恢复cout默认输出

shellching的专栏 5254

可以在线C++编译的工具站点

以下是搜集的几个可以在线编写代码并编译的站点 https://c.runoob.com/ https://www.tutorialspoint.com/compile_cpp11_online.php https://tio.run/#cpp-gcc https://wandbox.org/ https://gcc.godbolt.org/

shellching的专栏 5233

32位项目转64位一些问题记录

到现在还一直在用WindowsXPSP3+VS2008SP1;VS2010也装了,但是用了几次实在忍受不了其打开方案速度;VS2012也发布有一段时间了,与Win8一样采用了简约平面化的风格,只是不知道性能是否该改善,由于VS2012不支持XP系统,所以还没有详细体验。最近有几个项目需要移植到64位系统,这里记录一些问题,备个案。 1、项目引用的库都需要使用64位版本,有源码的自己编译64位版本

shellching的专栏 3619

一次Link2001错误的解决

项目中用到了一个静态库elBase.lib,该静态库中有用到一些windowsAPI,在项目的链接设置中增加对该库的引用后,编译时出现了一堆link2001错误: 1>elBase.lib(elServiceBase.obj) : error LNK2001: unresolved external symbol __imp__OpenSCManagerW@12 1>elBase.lib(

shellching的专栏 3551

fstream、ifstream、ofsream使用的一点总结

写程序时,有一段代码使用了fstream来创建、读写隐藏文件,但是却发现了一些奇怪的问题: 该创建时不创建,存在时却无法读取、修改,下面对这三类对象做了测试和总结,记录如下:   1 测试文件不存在时创建文件 ========================================================= fstream fs("aaa.txt", ios_base

shellching的专栏 3331

CMakelists生成VS工程

可通过GUI和命令行两种方式,GUI方式也就是设置目录,设置编译器,生成,比较简单直观不讲了。下面说下命令行方式。2 将包含CMakelists.txt文件的源码目录放在总目录中如mycode。5 进入build目录看到sln,打开用vs编译。4 打开命令行,进入budil目录执行。1 创建总目录如project。

shellching的专栏 2896

C++混合运算的类型提升和溢出

看看下面的运算结果各是多少呢?   #define MYUINT_MAX 4294967295 //0xffffffff #define MY_MAX 4294967296 //0x100000000 unsigned __int64 sz1=0, sz2=0; sz1 = MYUINT_MAX + 1; sz2 = MY_MAX + 1;   答案是【sz

shellching的专栏 2819

【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c

AI 编程时代,开发语言选型需综合考量AI适配度、编译速度、跨平台与学习成本。Python以海量语料和即时执行成首选,适用于AI/ML与原型开发;TypeScript凭借类型系统与秒级构建,成为前端及应用层主流;Go以极速编译与统一风格,契合AI代理高效迭代需求。Rust/C++虽性能强,但编译慢、学习成本高,不适于快速试错。综合推荐:优先选Python、TypeScript、Go,兼顾效率与生态,实现最优AI协作体验。

freewebsys的专栏 562

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 249
上一篇: Windows下文本工具试用记录
下一篇: 关于Bearer Token
UU_Yang
博客等级 码龄23年 926粉丝 213原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值