我一直在寻找一种方法来解决动态类型检查的缓慢问题。在你开始说我应该重新设计所有内容之前,让我告诉你,设计是在5年前决定的。我无法修复之后的所有400,000行代码(我希望可以),但我可以进行一些更改。我在类型识别上运行了这个小测试:
#include <iostream>
#include <typeinfo>
#include <stdint.h>
#include <ctime>
using namespace std;
#define ADD_TYPE_ID \
static intptr_t type() { return reinterpret_cast<intptr_t>(&type); }\
virtual intptr_t getType() { return type(); }
struct Base
{
ADD_TYPE_ID;
};
template <typename T>
struct Derived : public Base
{
ADD_TYPE_ID;
};
int main()
{
Base* b = new Derived<int>();
cout << "Correct Type: " << (b->getType() == Derived<int>::type()) << endl; // true
cout << "Template Type: " << (b->getType() == Derived<float>::type()) << endl; // false
cout << "Base Type: " << (b->getType() == Base::type()) << endl; // false
clock_t begin = clock();
{
for (size_t i = 0; i < 100000000; i++)
{
if (b->getType() == Derived<int>::type())
Derived <int>* d = static_cast<Derived<int>*> (b);
}
}
clock_t end = clock();
double elapsed = double(end - begin) / CLOCKS_PER_SEC;
cout << "Type elapsed: " << elapsed << endl;
begin = clock();
{
for (size_t i = 0; i < 100000000; i++)
{
Derived<int>* d = dynamic_cast<Derived<int>*>(b);
if (d);
}
}
end = clock();
elapsed = double(end - begin) / CLOCKS_PER_SEC;
cout << "Type elapsed: " << elapsed << endl;
begin = clock();
{
for (size_t i = 0; i < 100000000; i++)
{
Derived<int>* d = dynamic_cast<Derived<int>*>(b);
if ( typeid(d) == typeid(Derived<int>*) )
static_cast<Derived<int>*> (b);
}
}
end = clock();
elapsed = double(end - begin) / CLOCKS_PER_SEC;
cout << "Type elapsed: " << elapsed << endl;
return 0;
}
似乎使用类id(上面第一次解决方案)将是在运行时进行类型检查的最快方法。 这会导致线程出现问题吗?有没有更好的方法在运行时检查类型(没有太多的重新分解)?
编辑我可能还要补充一点,这需要与TI编译器配合使用,目前只支持'03
答案 0 :(得分:3)
首先请注意,dynamic_cast
和RTTI之间存在很大差异:演员告诉您是否可以将基础对象视为某些进一步派生,但不一定是派生的宾语。 RTTI告诉您精确的派生类型。当然,前者更强大,更昂贵。
那么,如果您具有多态层次结构,则可以通过两种自然方式在类型上进行选择。他们是不同的;使用实际适用的那个。
void method1(Base * p)
{
if (Derived * q = dynamic_cast<Derived *>(p))
{
// use q
}
}
void method2(Base * p)
{
if (typeid(*p) == typeid(Derived))
{
auto * q = static_cast<Derived *>(p);
// use q
}
}
另请注意,如果基类是虚拟基础,则通常不提供方法2。如果您的类不是多态的,那么这两种方法都不适用。
在快速测试中,我发现方法2明显快于基于手动ID的解决方案,后者反过来比动态强制解决方案(方法1)快。
答案 1 :(得分:1)
如何比较类的虚函数表?
快速而肮脏的概念证明:
void* instance_vtbl(void* c)
{
return *(void**)c;
}
template<typename C>
void* class_vtbl()
{
static C c;
return instance_vtbl(&c);
}
// ...
begin = clock();
{
for (size_t i = 0; i < 100000000; i++)
{
if (instance_vtbl(b) == class_vtbl<Derived<int>>())
Derived <int>* d = static_cast<Derived<int>*> (b);
}
}
end = clock();
elapsed = double(end - begin) / CLOCKS_PER_SEC;
cout << "Type elapsed: " << elapsed << endl;
使用Visual C ++的/Ox
切换,这比type
/ getType
技巧快3倍。
答案 2 :(得分:0)
鉴于此类代码
class A {
};
class B : public A {
}
A * a;
B * b = dynamic_cast<B*> (a);
if( b != 0 ) // do something B specific
多态(对吗?)修复它的方法是这样的
class A {
public:
virtual void specific() { /* do nothing */ }
};
class B : public A {
public:
virtual void specific() { /* do something B specific */ }
}
A * a;
if( a != 0 ) a->specific();
答案 3 :(得分:0)
当MSVC 2005首次推出时,dynamic_cast&lt;&gt;对于64位代码比32位代码要慢得多。我们想要一个快速简单的解决方案。这就是我们的代码。它可能违反了各种好的设计规则,但转换为删除dynamic_cast&lt;&gt;可以使用脚本自动完成。
class dbbMsgEph {
public:
virtual dbbResultEph * CastResultEph() { return 0; }
virtual const dbbResultEph * CastResultEph() const { return 0; }
};
class dbbResultEph : public dbbMsgEph {
public:
virtual dbbResultEph * CastResultEph() { return this; }
virtual const dbbResultEph * CastResultEph() const { return this; }
static dbbResultEph * Cast( dbbMsgEph * );
static const dbbResultEph * Cast( const dbbMsgEph * );
};
dbbResultEph *
dbbResultEph::Cast( dbbMsgEph * arg )
{
if( arg == 0 ) return 0;
return arg->CastResultEph();
}
const dbbResultEph *
dbbResultEph::Cast( const dbbMsgEph * arg )
{
if( arg == 0 ) return 0;
return arg->CastResultEph();
}
我们曾经有过
dbbMsgEph * pMsg;
dbbResultEph * pResult = dynamic_cast<dbbResultEph *> (pMsg);
我们将其更改为
dbbResultEph * pResult = dbbResultEph::Cast (pMsg);
使用简单的sed(1)脚本。虚拟函数调用非常有效。
答案 4 :(得分:0)
//在发布模块(VS2008)中这是真的:
cout << "Base Type: " << (b->getType() == Base::type()) << endl;
我想这是因为优化。所以我改变了Derived :: type()的实现
template <typename T>
struct Derived : public Base
{
static intptr_t type()
{
cout << "different type()" << endl;
return reinterpret_cast<intptr_t>(&type);
}
virtual intptr_t getType() { return type(); }
};
然后它有所不同。如果使用这种方法怎么处理呢???