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( ...
随机推荐
- 【BZOJ3680】吊打XXX(模拟退火)
[BZOJ3680]吊打XXX(模拟退火) 题面 BZOJ 题解 模拟退火... 就是模拟退火 然后这题有毒 各种调参数之后终于\(AC\)了.. 这种题就是玄学呀... 温度要调大 最后跑完还要向四 ...
- BSGS算法(大步小步算法)
计算\(y^x ≡ z \ mod\ p\) 中 \(x\) 的解. 这个模板是最小化了\(x\) , 无解输出\(No \ Solution!\) map<ll,ll>data; ll ...
- Python魔法方法(转发整合)
如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的. __new__: 是一个对象实例化时调用的第一 ...
- Win10下用Anaconda安装TensorFlow
什么是Anaconda anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项.它是一个用python开发机器学习的必备工具. 什么是ten ...
- 原来你是这样的Websocket--抓包分析
之前自己一个人负责完成了公司的消息推送服务,和移动端配合完成了扫码登录.订单消息推送.活动消息广播等功能.为了加深自己对Websocket协议的理解,自己通过进行抓包的方式学习了一番.现在分享出来,希 ...
- FJUT16级第一周寒假作业题解J题
题目链接:http://210.34.193.66:8080/vj/Contest.jsp?cid=160#P9 涨姿势之区间刷新 TimeLimit:2000MS MemoryLimit:128M ...
- linux 记录用户操作日志
将以下加入到/etc/profile 最后 history USER_IP=`who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]/ ...
- java容器类3:set/HastSet/MapSet深入解读
介绍 Set:集合,是一个不包含重复数据的集合.(A collection that contains no duplicate elements. ) set中最多包含一个null元素,否者包含了两 ...
- 基于python语言的签名算法
在wiki上看完接口文档根据传入的参数来查看返回的测试结果,测试结果提示缺少参数,找开发小伙伴沟通,原来发现缺少公共参数.找开发拿到公共参数的接口文档,发现里面传入的参数包含时间戳和签名. 时间戳:姑 ...
- 温故而知新--hashtable
哈希在实际使用中主要是当作私有内存,对数据进行插入和查找,哈希数据元素越多,操作的时候消耗的性能就越到,最明显的是当数据元素达到哈希的容量大小时,插入数据冲突概率会变大,并慢慢的退化为数组. 本例子中 ...