有趣的动态转换

简介: 有趣的动态转换

缘起

最近,在项目代码中看到一个非常神奇的类型转换—— 类型A 的指针居然能动态转换成另外一个完全没有任何关系的类指针。这…… 完全颠覆了我的认知。

为了进一步了解这个神奇的操作,我特意模拟了项目代码中的情形,一起来看看吧。

代码简介

BaseABaseB 是两个基类,NewA 继承自 BaseANewB 继承自 BaseBTestB() 会在堆上 new 一个 NewB 的对象,但是会强制转换成 BaseA 类型的指针并返回(这个操作太逆天,大家一定不要在项目代码中这么玩儿)。main() 函数中模拟使用和释放。背景介绍完毕,看代码。

测试代码 1

#include "stdafx.h"
#include "stdlib.h"

class BaseA
{
public:
    virtual void AA() = 0;
    virtual ~BaseA() {};
};

class NewA : public BaseA
{
public:
    ~NewA() { printf(__FUNCTION__ "\n"); }
    virtual void AA() override { printf(__FUNCTION__ "\n"); }
};

class BaseB
{
public:
    virtual void BB() = 0;
    virtual ~BaseB() {};
};

class NewB : public BaseB
{
public:
    virtual ~NewB() { printf(__FUNCTION__ "\n"); }
    virtual void BB() override { printf(__FUNCTION__ "\n"); }
};

BaseA* TestB()
{
    NewB *pNewB = new NewB();
    return (BaseA*)pNewB;
}

int _tmain(int argc, _TCHAR* argv[])
{
    BaseA* pBaseA = TestB();
    BaseB *pBaseB = (BaseB *)pBaseA;

    delete(pBaseA);

    system("pause");
    return 0;
}

上面的代码有什么问题吗?运行结果是:一切正常。

example1.png

but……

测试代码 2

相对于 测试代码1测试代码2 中调用了虚函数,只改变了 main 中的调用部分。

int _tmain(int argc, _TCHAR* argv[])
{
    BaseA* pBaseA = TestB();
    BaseB *pBaseB = (BaseB *)pBaseA;

    // added part: call virtual functions
    pBaseA->AA();
    pBaseB->BB();

    delete(pBaseA);

    system("pause");
    return 0;
}

上面的代码会输出什么呢?

example2.png

pBaseA->AA() 的输出结果是 NewB::BB。有些“意外”,但是却在情理之中。下一篇会比较详细的剖析虚函数调用机制,知道运作及之后,再回过头来看这个问题就很容易理解了!

测试代码 3

相对于 测试代码2测试代码 3 只为 BaseB 增加了一个名为 PerfectFunctionName() 的接口,并在子类NewB 中实现了这个接口。其它部分不变。这里只列出变化的部分。

#include "stdafx.h"
#include "stdlib.h"

class BaseB
{
public:
    virtual void BB() = 0;
    virtual void PerfectFunctionName() = 0; // Added part
    virtual ~BaseB() {};
};

class NewB : public BaseB
{
public:
    virtual ~NewB() { printf(__FUNCTION__ "\n"); }
    virtual void BB() override { printf(__FUNCTION__ "\n"); }
    virtual void PerfectFunctionName() override { printf(__FUNCTION__ "\n"); } // Added part
};

最新代码运行结果会是什么样的呢?

example3.png

上图是 32debug 版程序运行时的输出结果。终于报了异常!release 版运行后也会报异常!

example3-release.png

从测试结果可知,这种做法在某些情况下确实可以正常运行,但是某些情况下会得到一些奇怪的结果,甚至会发生崩溃。这些现象背后的原因我想留到后面几篇文章分析。感兴趣的小伙伴儿可以手动测试并分析。

结论

永远不要做这种危险的 SAO 操作!

More

强烈推荐大家看一下《深度探索 C++ 对象模型》 这本书!手头儿还没有?没问题,赶紧关注公众号【比目信息】,后台回复【抽奖】就有机会免费得到一本。2020 年 9 月 13 日开奖!

相关文章
|
安全 Shell Android开发
Android系统 init.rc sys/class系统节点写不进解决方案和原理分析
Android系统 init.rc sys/class系统节点写不进解决方案和原理分析
1082 0
|
Unix 网络安全 iOS开发
Mac 电脑如何安装Wireshark?
Mac 电脑如何安装Wireshark?
1492 0
Mac 电脑如何安装Wireshark?
|
C++
C++中类的接口与实现分离的技术性探讨
C++中类的接口与实现分离的技术性探讨
339 1
|
SQL XML Java
8、Mybatis-Plus 分页插件、自定义分页
这篇文章介绍了Mybatis-Plus的分页功能,包括如何配置分页插件、使用Mybatis-Plus提供的Page对象进行分页查询,以及如何在XML中自定义分页SQL。文章通过具体的代码示例和测试结果,展示了分页插件的使用和自定义分页的方法。
8、Mybatis-Plus 分页插件、自定义分页
|
SQL 搜索推荐 关系型数据库
MySQL 如何实现 ORDER BY 排序?
本文详细解析了MySQL中`ORDER BY`的实现原理及优化方法。通过解析与优化、执行及多种优化技术,如索引利用、内存排序、外部排序等,帮助你提升排序性能。了解其背后的机制,可显著优化查询效率。
739 4
|
数据可视化 Python
Matplotlib 教程 之 Matplotlib 饼图 1
使用 Matplotlib 库中的 `pyplot` 模块 `pie()` 方法来绘制饼图,并详细解释了 `pie()` 方法的参数,包括数据输入 `x`、扇区间距 `explode`、标签 `labels`、颜色 `colors`、百分比格式 `autopct`、标签距离 `labeldistance`、阴影 `shadow`、半径 `radius`、起始角度 `startangle`、逆时针方向 `counterclock`、扇形属性 `wedgeprops`、文本标签属性 `textprops`、饼图中心位置 `center`
210 2
|
测试技术 定位技术 项目管理
一图搞懂,全流程项目管理实践地图,驱动:市场-研发-售后
为了实现市场运营、产品研发与售后服务的有效协作,YesDev项目管理工具提供了一个全面的解决方案。它覆盖从项目成单到交付的全过程,通过清晰地划分市场运营、产品研发和售后服务三个主要阶段,加上组织管理作为基础设施,形成3+1的管理体系。 YesDev提供了一套从市场到售后的全流程管理方案,帮助企业实现跨部门高效协作。
|
机器学习/深度学习 文字识别 前端开发
基于 Spring Boot 3.3 + OCR 实现图片转文字功能
【8月更文挑战第30天】在当今数字化信息时代,图像中的文字信息越来越重要。无论是文档扫描、名片识别,还是车辆牌照识别,OCR(Optical Character Recognition,光学字符识别)技术都发挥着关键作用。本文将围绕如何使用Spring Boot 3.3结合OCR技术,实现图片转文字的功能,分享工作学习中的技术干货。
1631 2
|
关系型数据库 MySQL
启动mysql时报错"/etc/init.d/mysqld: Permission denied"
请谨慎操作,并根据你的具体情况选择适当的解决方法。如果问题仍然存在,你可能需要查看MySQL的文档或寻求进一步的支持。
960 1
|
Python
用Python优雅地写出数学表达式的LaTeX代码
用Python优雅地写出数学表达式的LaTeX代码
656 1
用Python优雅地写出数学表达式的LaTeX代码

热门文章

最新文章