C++高效安全的运行时动态类型转换

关键字:static_cast,dynamic_cast,fast_dynamic_cast,VS 2015。

OS:Window 10。

C++类之间类型转换有:static_cast、dynamic_cast、reinterpret_cast、和const_cast。

static_cast - 编译时类型检查。如果没有继承关系的类之间转换编译不通过。优点是快,缺点是从父类转换到子类不安全的。

dynamic_cast - 运行时类型检查。可以父子之间转换,也可以兄弟之间转换。优点是安全,缺点是运行时效率低。

reinterpret_cast - 强制转换。最不安全。只有特定场合才能使用。

const_cast - const类型和非const类型互转。

一般从父类转换到子类,或兄弟类之间转换使用dynamic_cast是正确的选择。但是对于大型应用程序的底层开发,dynamic_cast的性能问题就暴露出来了,使用static_cast又不能保证安全,这时就需要自己实现一套高效安全的运行时动态类型转换。

基于虚函数+类型检查的类型转换

1. 为每个类实现classType和queryObject方法。运行时,通过虚函数queryObject调用以及在queryObject里面检查classType来找到合适的对象。具体实现如下:

    class A
    {
    public:
        static const char* classType();
        virtual void* queryObject(const char* classType) const;
    };

    const char* A::classType()
    {
        const char* s_classType = "A";
        return s_classType;
    }

    void* A::queryObject(const char* classType) const
    {
        if (classType == A::classType())
            return const_cast<A*>(this);

        return nullptr;
    }

    class B
    {
    public:
        static const char* classType();
        virtual void* queryObject(const char* classType) const;
    };

    const char* B::classType()
    {
        const char* s_classType = "B";
        return s_classType;
    }

    void* B::queryObject(const char* classType) const
    {
        if (classType == B::classType())
            return const_cast<B*>(this);

        return nullptr;
    }

    class C : public A, public B
    {
    public:
        static const char* classType();
        void* queryObject(const char* classType) const override;
    };

    const char* C::classType()
    {
        const char* s_classType = "C";
        return s_classType;
    }

    void* C::queryObject(const char* classType) const
    {
        if (classType == C::classType())
            return const_cast<C*>(this);

        if (void* res = A::queryObject(classType))
            return res;

        if (void* res = B::queryObject(classType))
            return res;

        return nullptr;
    }

    class D : public A
    {
    public:
        static const char* classType();
        void* queryObject(const char* classType) const override;
    };

    const char* D::classType()
    {
        const char* s_classType = "D";
        return s_classType;
    }

    void* D::queryObject(const char* classType) const
    {
        if (classType == D::classType())
            return const_cast<D*>(this);

        return A::queryObject(classType);
    }

    template <typename To, typename From>
    To* fast_dynamic_cast(const From* from)
    {
        if (!from)
            return nullptr;

        return static_cast<To*>(from->queryObject(To::classType()));
    }

2. new对象C,用指针A指向C对象。C继承于A和B。

    A* a = new C();
    cout << "A* a = new C();" << endl;

3. 测试类型转换从A到C,计时和检验转换结果。从测试结果看,dyanmic_cast、static_cast、fast_dynamic_cast结果都正确,static_cast效率最高,fast_dynamic_cast其次,dyanmic_cast效率最差。

    cout << "===== cast from pointer A to pointer C, should be not null =====" << endl;

    C* c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = dynamic_cast<C*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

    c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = static_cast<C*>(a);
    }
    stop = clock();
    cout << "static_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

    c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = fast_dynamic_cast<C>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

测试结果:

===== cast from pointer A to pointer C, should be not null =====
dynamic_cast from A to C: 00000202D48C9FE0, Time: 2227
static_cast from A to C: 00000202D48C9FE0, Time: 0
fast_dynamic_cast from A to C: 00000202D48C9FE0, Time: 199

4.  测试类型转换从A到B,计时和检验转换结果。从测试结果看,static_cast编译不通过,dyanmic_cast、fast_dynamic_cast结果都正确,fast_dynamic_cast效率比dyanmic_cast高。

    cout << "\n===== cast from pointer A to pointer B, should be not null =====" << endl;

    B* b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        b = dynamic_cast<B*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl;

    b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        //b = static_cast<B*>(a); //compiler error
    }
    stop = clock();
    cout << "static_cast from A to B: " << "compiler error" << endl;

    b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        b = fast_dynamic_cast<B>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl;

测试结果:

===== cast from pointer A to pointer B, should be not null =====
dynamic_cast from A to B: 000001D65F2FA308, Time: 2927
static_cast from A to B: compiler error
fast_dynamic_cast from A to B: 000001D65F2FA308, Time: 208

5. 测试类型转换从A到D,计时和检验转换结果。从测试结果看,static_cast结果不正确,应为空指针,dyanmic_cast、fast_dynamic_cast结果都正确,fast_dynamic_cast效率比dyanmic_cast高。

    cout << "\n===== cast from pointer A to pointer D, should be null =====" << endl;

    D* d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = dynamic_cast<D*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

    d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = static_cast<D*>(a);
    }
    stop = clock();
    cout << "static_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

    d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = fast_dynamic_cast<D>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

测试结果:

===== cast from pointer A to pointer D, should be null =====
dynamic_cast from A to D: 0000000000000000, Time: 3534
static_cast from A to D: 0000026050C6D310, Time: 0
fast_dynamic_cast from A to D: 0000000000000000, Time: 227

总结:根据以上测试结果,fast_dynamic_cast和dynamic_cast行为完全一致,并且效率上fast_dynamic_cast比dynamic_cast快10倍以上,在追求性能的底层开发完全可以用fast_dynamic_cast代替dynamic_cast。

附完整代码:

#include <string>
#include <time.h>
#include <iostream>
#include <unordered_map>

using namespace std;

namespace
{
    class A
    {
    public:
        static const char* classType();
        virtual void* queryObject(const char* classType) const;
    };

    const char* A::classType()
    {
        const char* s_classType = "A";
        return s_classType;
    }

    void* A::queryObject(const char* classType) const
    {
        if (classType == A::classType())
            return const_cast<A*>(this);

        return nullptr;
    }

    class B
    {
    public:
        static const char* classType();
        virtual void* queryObject(const char* classType) const;
    };

    const char* B::classType()
    {
        const char* s_classType = "B";
        return s_classType;
    }

    void* B::queryObject(const char* classType) const
    {
        if (classType == B::classType())
            return const_cast<B*>(this);

        return nullptr;
    }

    class C : public A, public B
    {
    public:
        static const char* classType();
        void* queryObject(const char* classType) const override;
    };

    const char* C::classType()
    {
        const char* s_classType = "C";
        return s_classType;
    }

    void* C::queryObject(const char* classType) const
    {
        if (classType == C::classType())
            return const_cast<C*>(this);

        if (void* res = A::queryObject(classType))
            return res;

        if (void* res = B::queryObject(classType))
            return res;

        return nullptr;
    }

    class D : public A
    {
    public:
        static const char* classType();
        void* queryObject(const char* classType) const override;
    };

    const char* D::classType()
    {
        const char* s_classType = "D";
        return s_classType;
    }

    void* D::queryObject(const char* classType) const
    {
        if (classType == D::classType())
            return const_cast<D*>(this);

        return A::queryObject(classType);
    }

    template <typename To, typename From>
    To* fast_dynamic_cast(const From* from)
    {
        if (!from)
            return nullptr;

        return static_cast<To*>(from->queryObject(To::classType()));
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    A* a = new C();
    cout << "A* a = new C();" << endl;

    clock_t start, stop;
    const int count = 100000000;

    cout << "===== cast from pointer A to pointer C, should be not null =====" << endl;

    C* c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = dynamic_cast<C*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

    c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = static_cast<C*>(a);
    }
    stop = clock();
    cout << "static_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

    c = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        c = fast_dynamic_cast<C>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;

    cout << "\n===== cast from pointer A to pointer B, should be not null =====" << endl;

    B* b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        b = dynamic_cast<B*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl;

    b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        //b = static_cast<B*>(a); //compiler error
    }
    stop = clock();
    cout << "static_cast from A to B: " << "compiler error" << endl;

    b = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        b = fast_dynamic_cast<B>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl;

    cout << "\n===== cast from pointer A to pointer D, should be null =====" << endl;

    D* d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = dynamic_cast<D*>(a);
    }
    stop = clock();
    cout << "dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

    d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = static_cast<D*>(a);
    }
    stop = clock();
    cout << "static_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

    d = nullptr;
    start = clock();
    for (int i = 0; i < count; i++)
    {
        d = fast_dynamic_cast<D>(a);
    }
    stop = clock();
    cout << "fast_dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;

    delete a;
    a = nullptr;
    return 0;
}

原文地址:https://www.cnblogs.com/ldlchina/p/8698346.html

时间: 2024-12-18 08:19:02

C++高效安全的运行时动态类型转换的相关文章

LINQ to SQL 运行时动态构建查询条件

在进行数据查询时,经常碰到需要动态构建查询条件.使用LINQ实现这个需求可能会比以前拼接SQL语句更麻烦一些.本文介绍了3种运行时动态构建查询条件的方法. 本文中的例子最终实现的都是同一个功能,从Northwind数据库Customers表中搜索出CompanyName列带有keywords中任意元素的项.keywords是个字符串数组,该数组长度在编译时是不确定的.思路及方法说明写在代码注释中. 1.表达式树 1   public static IEnumerable<Customers> 

java-基础入门-泛型数组列表-解决运行时动态更改数组的问题

泛型数组列表,主要是为了解决运行时动态更改数组的问题 平常我们会定义一个部门里面的人员的一个数组, 但是在实际业务当中,这个数组的大小往往是不确定的, 如果定义过大,那么会浪费空间,定义过小,又不够用, 因为为了解决运行时动态更改数组的问题,我们提出下面的解决方案. package com.ray.object; import java.util.ArrayList; /** * 泛型数组列表 ,解决运行时动态更改数组的问题 * * @author ray * @since 2015-05-04

C# 在运行时动态创建类型

C# 在运行时动态的创建类型,这里是通过动态生成C#源代码,然后通过编译器编译成程序集的方式实现动态创建类型 public static Assembly NewAssembly() { //创建编译器实例. provider = new CSharpCodeProvider(); //设置编译参数. cp = new CompilerParameters(); cp.GenerateExecutable = false; cp.GenerateInMemory = true; // Gener

浏览器客户端智能自动化:如何取得页面中JavaScript运行时动态生成的URL?

浏览器客户端智能自动化:如何取得页面中JavaScript运行时动态生成的URL? 需求 "页面智能拼接"指的是通过启发式查询DOM树,判断出"下一页"链接,取出其href属性.Chromium的官方插件DOM Distiller完成类似的工作,主要目的就是为了将多页点击流程变成单页的Ajax连续阅读体验. 问题是,现在有些网站为了阻止浏览器客户端这么做,将href属性设置为"#"(或javascript:void()),然后在其onclick事

Android运行时动态权限获取

运行时动态权限简介 在targetSdkVersion小于23的应用中默认授予AndroidManifest中声明的所有权限,而无需手动授予.当targetSdkVersion在23或以上时,应用默认不会授予"Dangerous"级别的权限,Android默认只要授予该组一个权限即可获得该组的所有权限. 动态权限获取 下面将演示如何通过运行时权限获取来调用系统相机进行拍照和录制视频 首先以targetSdkVersion 23来编译调试应用,要调用相机拍照并存储需要在在AndroidM

C#在运行时动态创建类型的实现方法

本文实例讲述了C#在运行时动态创建类型的实现方法.是C#项目开发中很实用的技巧.分享给大家供大家参考.具体分析如下: 具体来说, C# 在运行时动态的创建类型是通过动态生成C#源代码,然后通过编译器编译成程序集的方式实现动态创建类型的 . 主要功能代码如下: public static Assembly NewAssembly() { //创建编译器实例. provider = new CSharpCodeProvider(); //设置编译参数. cp = new CompilerParame

转: gcc 指定运行时动态库路径

gcc 指定运行时动态库路径 Leave a reply 由于种种原因,Linux 下写 c 代码时要用到一些外部库(不属于标准C的库),可是由于没有权限,无法将这写库安装到系统目录,只好安装用户目录下如 /home/youname/lib,可是怎么编译才能让程序正常编译,并且正常运行呢.这样使用gcc:gcc -I/path/to/include/dir -L/path/to/lib/dir -llibname -Wl,-rpath,/path/to/lib/dir -o test test.

SpringBoot运行时动态添加数据源

此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!! 一.多数据源应用场景: 1.配置文件配置多数据源,如默认数据源:master,数据源1:salve1...,运行时动态切换已配置的数据源(master.salve1互相切换),无法在运行时动态添加配置文件中未配置的数据源. 2.配置一个默认数据源,运行时动态添加新数据源使用(本博客适用于此场景) 二.解决方案: Spring提供了AbstractRoutingDataSource用于动态路由数据源,第一种场景

JMeter非GUI方式运行时动态设置线程组及传参

在使用JMeter进行性能测试自动化时,可能会有如下需求: 1.指定运行多少线程,指定运行多少次: 2.访问的目标地址变化了,端口也变化了,需要重新指定. 上面的需求如果有GUI方式运行,这都不是问题,直接在脚本上进行修改即可以了. 但是性能测试自动化是以非GUI方式运行的,如果要修改测试计划就比较麻烦了. 下面来说说如何简单的搞定这些问题: 1.指定运行多少线程   我们知道JMeter测试计划在运行Sampler之前先加载运行属性(jmeter.properties,system.prope