我需要运行时多态性,所以我使用了dynamic_cast
。
但现在我面临两个问题——dynamic_cast
非常慢!(请向下滚动查看基准测试。)
长话短说,最终我使用了static_cast
来解决这个问题:
struct Base
{
virtual ~Base() { }
virtual int type_id() const = 0;
template<class T>
T *as()
{ return this->type_id() == T::ID ? static_cast<T *>(this) : 0; }
template<class T>
T const *as() const
{ return this->type_id() == T::ID ? static_cast<T const *>(this) : 0; }
};
struct Derived : public Base
{
enum { ID = __COUNTER__ }; // warning: can cause ABI incompatibility
int type_id() const { return ID; }
};
int main()
{
Base const &value = Derived();
Derived const *p = value.as<Derived>(); // "static" dynamic_cast
}
但我肯定不是第一个遇到这个问题的人,所以我认为询问一下可能是值得的:
除了像这样想出自己的解决方案之外,在将来解决这个问题时是否有可以使用的知名模式/库?
示例基准测试
为了感受一下我正在谈论什么,请尝试下面的代码 - 在我的机器上,dynamic_cast
比简单的virtual
调用慢了约15倍(使用下面的代码,110毫秒对1620毫秒):
#include <cstdio>
#include <ctime>
struct Base { virtual unsigned vcalc(unsigned i) const { return i * i + 1; } };
struct Derived1 : public Base
{ unsigned vcalc(unsigned i) const { return i * i + 2; } };
struct Derived2 : public Derived1
{ unsigned vcalc(unsigned i) const { return i * i + 3; } };
int main()
{
Base const &foo = Derived2();
size_t const COUNT = 50000000;
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = foo.vcalc(n);
printf("virtual call: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = dynamic_cast<Derived1 const &>(foo).vcalc(n);
printf("virtual call after dynamic_cast: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
return 0;
}
当我仅仅去掉单词
virtual 并将 dynamic_cast 更改为 static_cast 时,运行时间为79毫秒 - 因此虚函数调用只比静态调用慢约25%!
dynamic_cast
这么多次以至于它会影响性能,那么您可能需要重新审视您的设计。 - casablancadynamic_cast
缓慢的众所周知的解决方案是什么?不使用它。运行时多态和dynamic_cast
几乎是相反的概念:多态是能够获取对象的引用并在不关心类型的情况下使用它,而dynamic_cast
则关心类型,因为您无法通过特征进行多态操作。最好的建议已经由 @casablanca 给出:重新审视设计。如果您正在使用dynamic_cast
,目标不是使其更快,而是将其从设计中删除。 - David Rodríguez - dribeasisa
,dyn_cast
等,它们对类层次结构施加了限制,并且需要为每个类进行一些手动工作,但效率非常高(只需加载成员并将其与常量进行比较)。这可能有点过度设计,或者限制可能太严格,但仍然非常有趣。 - user395760