C++中高效的运行时类型检查

Efficient run-time type checking in C++

本文关键字:类型 检查 运行时 高效 C++      更新时间:2023-10-16

我有两个名为"表达式"和"BinExp"的类作为以下代码:

class Expression
{
public:
    virtual BinExp* IsBinaryExp() { return NULL; }
};
class BinExp : public Expression
{
public:
    virtual BinExp* IsBinaryExp() { return this; }
};

例如,我的指针变量类型为 Expression* 但初始化为 new BinExp 并作为参数发送到 analyse 函数,如以下代码所示:

int main()
{
    Expression* e = new BinExp;
    analyse(e);
}

analyse 函数中,我需要知道e是指向Expression类型还是BinExp类型的指针。在我手里,有三种方法可以做到这一点。

第一:

BinExp* be = e->IsBinaryExp();
if ( be )
{
    printf("Yes, `e` is a binary expressionn");
}

第二:

BinExp* be = dynamic_cast<BinExp*>(e);
if ( be )
{
    printf("Yes, `e` is a binary expressionn");
}

第三个:

if ( typeid(*e) == typeid(BinExp) )
{
    BinExp* be = e->IsBinaryExp(); // or dynamic_cast<BinExp*>(e);
    printf("Yes, `e` is a binary expressionn");
}

但是我想知道当我需要在性能很重要的循环中频繁执行检查时,哪种方法(或任何其他方法)会更有效。任何建议,我将不胜感激。

最快的方法是保留一个成员变量,比如一个枚举,然后在基类中定义一个内联 getter,然后你可以比较结果是否是你期望的。

示例(未编译,可能会发生一些错误):

  enum eExpTypes {
        ET_UNDEFINED,
        ET_BINARY
  }
  class Expresion
  {
      protected:
            eExpTypes myType;
      public:
            Expresion(): myType(ET_UNDEFINED){};
            inline eExpTypes getType(){return myType;};
  }
  class BinExpresion : public Expresion
  {
     public:
            BinExpresion():myType(ET_BINARY){};
  }

性能提升 :

  • 您将取出两个数字:从指针到 VFPtable,从 VFPTABLE 到 函数
  • 如果类型函数是唯一的虚拟函数,您的班级规模会更小

动态转换通常比制作自己的类型检查机制慢,因此在您的 3 个示例中,第一个应该是最快的。

最快的是:

e->printIsBinExp();

你在哪里制作那个虚拟方法,要么打印,要么是一个noop。

我只是在开玩笑。virtual方法的要点是封装不同类型的对特定方法的处理 - 而不是编写一个程序来简单地对不同的运行时类型进行运行时切换。

假设dynamic_cast最快。然后你会写:

if (BinExp* be = dynamic_cast<BinExp*>(e)) {
    // ...
}
else if (UnExp* ue = dynamic_cast<UnExp*>(e)) {
    // ...
}
else if (TernExp* te = dynamic_cast<TernExp*>(e)) {
    // ...
}

希望不是。该代码将非常脆弱。您肯定想出这样的设计:

e->eval();

只是做正确的事情作为单一virtual调用。

数字 3 是最优雅的。

为了找出是否或哪一个是最有效的,可以使用一些简单的代码来测量每个案例的执行时间......

#include <iostream>
#include <chrono>
/*
Case
*/
int main()
{
    const clock_t begin_time = clock();
    // Case
    std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC;
    system("pause");
    return 0;
}