我有一个类型的层次结构--GenericClass和一些派生类,包括InterestingDerivedClass,GenericClass是多态的。有一个界面
interface ICallback {
virtual void DoStuff( GenericClass* ) = 0;
};
我需要实现。然后我想检测传递给ICallback :: DoStuff()的GenericClass *指针实际上是指向InterestingDerivedClass的指针的情况:
class CallbackImpl : public ICallback {
void DoStuff( GenericClass* param ) {
if( dynamic_cast<InterestingDerivedClass*>( param ) != 0 ) {
return; //nothing to do here
}
//do generic stuff
}
}
GenericClass和派生类不受我的控制,我只控制CallbackImpl。
我对dynamic_cast语句进行了定时 - 它需要大约1400个周期才可以接受,但看起来不是很快。我试着在调试器中读取dynamic_cast期间执行的内容的反汇编,并看到它需要很多指令。
因为我真的不需要指向派生类的指针,是否有更快的方法在运行时使用RTTI检测对象类型?也许某些特定于实现的方法只检查“是一个”关系但不检索指针?
答案 0 :(得分:12)
我总是将dynamic_cast
用作代码气味。您可以在所有情况下使用多态行为替换它并提高代码质量。在你的例子中,我会做这样的事情:
class GenericClass
{
virtual void DoStuff()
{
// do interesting stuff here
}
};
class InterestingDerivedClass : public GenericClass
{
void DoStuff()
{
// do nothing
}
};
class CallbackImpl : public ICallback {
void DoStuff( GenericClass* param ) {
param->DoStuff();
}
}
在您的情况下,您无法修改目标类,您正在编程为GenericClass
类型声明所隐含的合同。因此,你不可能做任何比dynamic_cast
更快的事情,因为其他任何东西都需要修改客户端代码。
答案 1 :(得分:5)
正如其他人所说,使用虚拟功能是一种很好的做法。使用它的另一个原因,不适用于你的情况,因为你不能添加音频,但我仍然值得一提 - 它可能比使用动态演员要快得多。在我用g ++完成的一些(不是非常严格的)测试中,虚函数比dynamic_cast高出4倍。
因为存在这样的差异,所以可能值得创建自己的继承层次结构,它包装了您无法控制的代码。您可以使用dynamic_cast(一次)创建包装器的实例,以决定要创建的派生类型,然后使用虚拟函数。
答案 2 :(得分:2)
对我来说,这看起来像是一个非常糟糕的设计。 (正如其他人所提到的,必须使用dynamic_cast
通常表明您遇到了设计问题。)但是,如果大多数代码都不在您的控制范围内,那么我认为没有太多可以解决的问题。
但不,我所知道的唯一通用解决方案是dynamic_Cast
。 typeid
只匹配派生的类型最多,可能在您的案例中有效。
另一方面,dynamic_cast
如此昂贵的原因可能是它必须遍历整个类层次结构。如果你有一个深层次结构,那就会变得昂贵(换句话说,通常情况下,没有深层次结构,但在C ++中尤其是)。
(当然第一次执行转换时,大多数类描述符查找可能都是缓存未命中,这可能会使您的基准测试偏差并使其看起来比它更昂贵)
答案 3 :(得分:1)
比较type_info
会更快吗? (在参数typeid
上调用param
)
答案 4 :(得分:1)
首先,不要过早优化。其次,如果您在内部查询对象的具体实现,则可能是您的设计出现了问题(想想双重调度)。
至于原始问题,向GetRuntimeType()
引入ICallback
函数会做得很好:请参阅MFC了解如何做到这一点。
答案 5 :(得分:1)
在具体的用例中,答案是使用虚函数。
但是,在某些情况下,您需要动态地向下转发。有一些技术可以使这个操作更快(或者更快,具体取决于编译器实现的智能程度dynamic_cast
),特别是如果你将自己限制为单继承。主要的想法是,如果你以某种方式知道确切的类型,static_cast
要快得多:
f(A* pA)
{
if (isInstanceOfB(pA))
{
B* pB = static_cast<B*>(pA);
// do B stuff...
}
}
当然,现在的问题是快速实现isInstanceOfB()。
例如,请参阅boost::type_traits。
答案 6 :(得分:1)
标准的dynamic_cast非常灵活,但通常非常慢,因为它可以处理您可能不感兴趣的许多角落情况。如果使用单个继承,则可以使用基于虚函数的简单实现替换它。
示例实施:
// fast dynamic cast
//! Fast dynamic cast declaration
/*!
Place USE_CASTING to class that should be recnognized by dynamic casting.
Do not forget do use DEFINE_CASTING near class definition.
*\note Function dyn_cast is fast and robust when used correctly.
Each class that should be used as target for dyn_cast
must use USE_CASTING and DEFINE_CASTING macros.\n
Forgetting to do so may lead to incorrect program execution,
because class may be sharing _classId with its parent and IsClassId
will return true for both parent and derived class, making impossible'
to distinguish between them.
*/
#define USE_CASTING(baseType) \
public: \
static int _classId; \
virtual size_t dyn_sizeof() const {return sizeof(*this);} \
bool IsClassId( const int *t ) const \
{ \
if( &_classId==t ) return true; \
return baseType::IsClassId(t); \
}
//! Fast dynamic cast root declaration
/*!
Place USE_CASTING_ROOT to class that should act as
root of dynamic casting hierarchy
*/
#define USE_CASTING_ROOT \
public: \
static int _classId; \
virtual size_t dyn_sizeof() const {return sizeof(*this);} \
virtual bool IsClassId( const int *t ) const { return ( &_classId==t ); }
//! Fast dynamic cast definition
#define DEFINE_CASTING(Type) \
int Type::_classId;
template <class To,class From>
To *dyn_cast( From *from )
{
if( !from ) return NULL;
if( from->IsClassId(&To::_classId) )
{
assert(dynamic_cast<To *>(from));
return static_cast<To *>(from);
}
return NULL;
}
那就是说,我完全同意其他人的看法,dynamic_cast很可疑,你最常能够以更清洁的方式实现同样的目标。也就是说,与goto类似,在某些情况下,它可能真正有用且更具可读性。
另一个注意事项:如果你说有问题的课程不受你的控制,这个解决方案对你没有帮助,因为它要求你修改课程(不多,只需添加几行,但你需要修改他们)。如果确实如此,则需要使用语言提供的内容,即dynamic_cast和typeinfo。
答案 7 :(得分:0)