有没有比使用dynamic_cast更快的方法在运行时检测对象类型?

时间:2009-06-16 08:57:22

标签: c++ visual-c++ dynamic polymorphism

我有一个类型的层次结构--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检测对象类型?也许某些特定于实现的方法只检查“是一个”关系但不检索指针?

8 个答案:

答案 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_Casttypeid只匹配派生的类型最多,可能在您的案例中有效。

另一方面,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)