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()
{
static 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()
{
static 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()
{
static 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()
{
static 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 = ; 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 = ; 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 = ; 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:
static_cast from A to C: 00000202D48C9FE0, Time:
fast_dynamic_cast from A to C: 00000202D48C9FE0, Time:
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 = ; 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 = ; 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 = ; 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:
static_cast from A to B: compiler error
fast_dynamic_cast from A to B: 000001D65F2FA308, Time:
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 = ; 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 = ; 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 = ; 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: , Time:
static_cast from A to D: 0000026050C6D310, Time:
fast_dynamic_cast from A to D: , Time:
总结:根据以上测试结果,fast_dynamic_cast和dynamic_cast行为完全一致,并且效率上fast_dynamic_cast比dynamic_cast快10倍以上,在追求性能的底层开发完全可以用fast_dynamic_cast代替dynamic_cast。
附完整代码:
#include "stdafx.h" #include <string>
#include <time.h>
#include <iostream> using namespace std; namespace
{
class A
{
public:
static const char* classType();
virtual void* queryObject(const char* classType) const;
template <typename From>
static A* queryObject(const From* from)
{
if (!from)
return nullptr; return static_cast<A*>(from->queryObject(A::classType()));
}
}; const char* A::classType()
{
static 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;
template <typename From>
static B* queryObject(const From* from)
{
if (!from)
return nullptr; return static_cast<B*>(from->queryObject(B::classType()));
}
}; const char* B::classType()
{
static 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;
template <typename From>
static C* queryObject(const From* from)
{
if (!from)
return nullptr; return static_cast<C*>(from->queryObject(C::classType()));
}
}; const char* C::classType()
{
static 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;
template <typename From>
static D* queryObject(const From* from)
{
if (!from)
return nullptr; return static_cast<D*>(from->queryObject(D::classType()));
}
}; const char* D::classType()
{
static 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)
{
return To::queryObject(from);
}
} int main()
{
A* a = new C();
cout << "A* a = new C();" << endl; clock_t start, stop;
const int count = ; cout << "===== cast from pointer A to pointer C, should be not null =====" << endl; C* c = nullptr;
start = clock();
for (int i = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 ;
}
C++高效安全的运行时动态类型转换的更多相关文章
- C# 在运行时动态创建类型
C# 在运行时动态的创建类型,这里是通过动态生成C#源代码,然后通过编译器编译成程序集的方式实现动态创建类型 public static Assembly NewAssembly() { //创建编译 ...
- LINQ to SQL 运行时动态构建查询条件
在进行数据查询时,经常碰到需要动态构建查询条件.使用LINQ实现这个需求可能会比以前拼接SQL语句更麻烦一些.本文介绍了3种运行时动态构建查询条件的方法.本文中的例子最终实现的都是同一个功能,从Nor ...
- 使用javassist运行时动态重新加载java类及其他替换选择
在不少的情况下,我们需要对生产中的系统进行问题排查,但是又不能重启应用,java应用不同于数据库的存储过程,至少到目前为止,还不能原生的支持随时进行编译替换,从这种角度来说,数据库比java的动态性要 ...
- 运行时动态库:not found 及介绍-linux的-Wl,-rpath命令
---此文章同步自我的CSDN博客--- 一.运行时动态库:not found 今天在使用linux编写c/c++程序时,需要用到第三方的动态库文件.刚开始编译完后,运行提示找不到动态库文件.我就 ...
- 转: gcc 指定运行时动态库路径
gcc 指定运行时动态库路径 Leave a reply 由于种种原因,Linux 下写 c 代码时要用到一些外部库(不属于标准C的库),可是由于没有权限,无法将这写库安装到系统目录,只好安装用户目录 ...
- [转] Java运行时动态生成class的方法
[From] http://www.liaoxuefeng.com/article/0014617596492474eea2227bf04477e83e6d094683e0536000 廖雪峰 / 编 ...
- 运行时动态伪造vsprintf的va_list
运行时动态伪造vsprintf的va_list #include <stdio.h> int main() { char* m = (char*) malloc(sizeof(int)*2 ...
- SpringBoot运行时动态添加数据源
此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!! 一.多数据源应用场景: 1.配置文件配置多数据源,如默认数据源:master,数据源1:salve1...,运行 ...
- 解决 Retrofit 多 BaseUrl 及运行时动态改变 BaseUrl ?
原文地址: juejin.im/post/597856- 解决Retrofit多BaseUrl及运行时动态改变BaseUrl(一) 解决Retrofit多BaseUrl及运行时动态改变BaseUrl( ...
随机推荐
- FCN小小实战
先说一下前期准备工作:自己的运行环境是Ubuntu16.04+caffe+CPU(这台电脑没有GPU)+python 关于python的搭建就不说了,网上随便一搜,很多参考资源.说一下我配置好caff ...
- 详解MySQL数据类型
原文地址http://www.cnblogs.com/xrq730/p/5260294.html,转载请注明出处,谢谢! 前言 很久没写文章,也有博友在我的有些文章中留言,希望我可以写一些文章,公司项 ...
- 一年iOS工作经验,如何一举拿下百度、美团、快手等Offer面经(附面试题)
前言: 先简单说说我最近的面试经历吧.面试的公司很多,大部分最后都能得到令人满意的结果,我将这些体会记录下来,面了这么多公司,如果不留下什么,那岂不是太浪费了.对于我来说,这也是一次自我检查,在这次面 ...
- CyQ.data MDataTable
前言 以前一两个月才出一篇,这三天有点变态地连续1天1篇(其实都是上周末两天写好的存货). 短期应该没有新的和此框架相关的文章要写了,这应该是最后一篇,大伙且看且珍惜. 前两篇讲数据库读写分享和分布式 ...
- WCF使用纯代码的方式进行服务寄宿
服务寄宿的目的是为了开启一个进程,为WCF服务提供一个运行的环境.通过为服务添加一个或者多个终结点,使之暴露给潜在的服务消费,服务消费者通过匹配的终结点对该服务进行调用,除去上面的两种寄宿方式,还可以 ...
- AJAX跨域问题解决思路
ajax跨域问题的解决思路主要分为3种: 1.浏览器限制解决思路:不让浏览器做出限制解决方法:通过指定参数,让浏览器不做跨域校验评价:价值不大,需要每个人都做改动,而且改动是客户端的改动 2.XHR请 ...
- bootstrap 栅格系统 自动隐藏
1 Container 顾名思义Container是栅格系统最外层的class,直接被container包裹的只能是row这个class.需要注意的是container自带左右各15px paddin ...
- JDK1.8源码(三)——java.lang.String 类
String 类也是java.lang 包下的一个类,算是日常编码中最常用的一个类了,那么本篇博客就来详细的介绍 String 类. 1.String 类的定义 public final class ...
- 云主机和vps的区别
云主机和vps的区别 近年来,IT行业开始热衷于各种云概念,而云主机就是其中之一,并且有越来越热之势.对普通用户而言,可能不太清楚云主机和VPS的区别,下面我们就来说说云主机和VPS到底有什么不同 ...
- 关于IPFS的热门问题
最近小编在公众号收到了一些提及比较高的问题,今天总结一下统一回答 目前网络上有一些对ipfs的解读五花八门,各式各样,有看好的,也有打击的,总之一项新技术诞生之初遇到的问题IPFS都遇到了. 问题 ...