有趣的动态转换

简介: 有趣的动态转换

缘起

最近,在项目代码中看到一个非常神奇的类型转换—— 类型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 日开奖!

相关文章
|
6月前
|
人工智能 UED
DynamiCrafter:可实现任意类型静态图像转化为逼真动态视频
【2月更文挑战第17天】DynamiCrafter:可实现任意类型静态图像转化为逼真动态视频
351 1
DynamiCrafter:可实现任意类型静态图像转化为逼真动态视频
|
6月前
|
存储 安全 C语言
动态储存方式与静态储存方式
动态储存方式与静态储存方式
62 1
|
6月前
模式间转换
模式间转换。
23 1
|
6月前
|
编译器 C++
47不同类型数据间的转换
47不同类型数据间的转换
33 0
|
存储 程序员 C语言
如何进行C++动态转换
如何进行C++动态转换
如何进行C++动态转换
|
前端开发 程序员 C#
【C#】通过扩展对象的方式,对字符串等数据类型进行数据进一步处理
在本篇文章中,我们讲一起了解下对象扩展的使用 在实际项目开发中,对象扩展使用的场景还是挺多的,比如:需要对时间值进行再处理,或者字符串中的斜杠(/)转为反斜杠(\)
115 0
|
存储 NoSQL 安全
|
JavaScript 前端开发 算法
从规范的角度解析对象 — 原始值转换
从规范的角度解析对象 — 原始值转换
145 0
从规范的角度解析对象 — 原始值转换
|
Java Maven Spring
蔡昊 - 如何高效而优雅的进行对象转换
简介 随着分层结构和DDD设计思想的普及,我们在后端开发过程中,会使用到VO/DO/DTO等等各种类型的模型对象,对象的转换工作也随着变得越来越多。而这种工作技术含量不高,却容易过多的出现在编码里,处理不当也会出现各种问题,那么如何更加高效而优雅的进行类型转换呢? 且花几分钟了解下笔者接下来要讲的一款Java代码生成器MapStruct——创建实现Java Bean之间转换的扩展映射器!
270 0