RTTI的价格是多少?


152

我了解使用RTTI会带来资源损失,但是它有多大?我看过的每个地方都只是说“ RTTI很昂贵”,但是它们都没有给出任何基准或定量的数据保护内存,处理器时间或速度。

那么,RTTI到底有多贵?我可能会在只有4MB RAM的嵌入式系统上使用它,因此每一位都很重要。

编辑:根据S. Lott的回答,如果我包括我实际在做的事情会更好。 我正在使用一个类来传递不同长度的数据,并且可以执行不同的操作,因此仅使用虚函数很难做到这一点。似乎使用了一些dynamic_cast s可以通过允许不同的派生类通过不同的级别传递而仍然允许它们采取完全不同的操作来解决此问题。

据我了解,dynamic_cast它使用RTTI,所以我想知道在有限的系统上使用RTTI的可行性。


1
从您的编辑开始-很多时候,当我发现自己要进行几次动态转换时,我意识到使用Visitor模式可以使事情再一次得到解决。那对你有用吗?
philsquared

4
我这样说-我刚开始dynamic_cast在C ++中使用,现在,当我用调试器“破坏”程序时,十分之九,它破坏了内部动态广播功能。该死的慢。
user541686 2012年

3
顺便说一句,RTTI =“运行时类型信息”。
Noumenon 2014年

Answers:


115

无论编译器如何,只要有能力,您都可以始终节省运行时

if (typeid(a) == typeid(b)) {
  B* ba = static_cast<B*>(&a);
  etc;
}

代替

B* ba = dynamic_cast<B*>(&a);
if (ba) {
  etc;
}

前者只涉及一项比较std::type_info; 后者必然涉及遍历继承树以及比较。

除此之外……就像大家所说的那样,资源使用是特定于实现的。

我同意所有人的意见,即出于设计原因,提交者应避免使用RTTI。然而,使用RTTI(主要是因为升压::任何的)很好的理由。记住,在常见的实现中了解其实际资源使用情况很有用。

我最近对GCC中的RTTI做了大量研究。

tl; dr:typeid(a) == typeid(b)在许多平台(Linux,BSD甚至嵌入式平台,但不是mingw32)上,GCC中的RTTI使用的空间可忽略不计且非常快。如果您知道自己将永远处于一个幸运的平台上,那么RTTI几乎是免费的。

粒度细节:

GCC倾向于使用特定的“与供应商无关”的C ++ ABI [1],并且始终将此ABI用于Linux和BSD目标[2]。对于支持此ABI且弱链接的平台typeid(),即使跨越动态链接边界,也为每种类型返回一致且唯一的对象。您可以测试&typeid(a) == &typeid(b),也可以仅依赖于便携式测试typeid(a) == typeid(b)实际上只是在内部比较指针。

在GCC首选的ABI中,类vtable 始终包含指向每个类型的RTTI结构的指针,尽管可能不使用它。因此,一个typeid()调用本身应该只花费不亚于任何其他虚函数表查找(同调用虚成员函数)的,和RTTI支持不应该使用任何额外的空间为每个对象。

据我所知,GCC使用的RTTI结构(是的所有子类std::type_info)除名称外,每种类型仅保留几个字节。我不清楚即使使用,名称也是否出现在输出代码中-fno-rtti。无论哪种方式,已编译二进制文件的大小更改都应反映运行时内存使用情况的更改。

一个快速实验(在64位Ubuntu 10.04上使用GCC 4.4.3)显示,-fno-rtti实际上,一个简单测试程序的二进制大小增加了几百个字节。在-g和的组合中始终会发生这种情况-O3。我不确定为什么会增加大小;一种可能性是,在没有RTTI的情况下,GCC的STL代码的行为会有所不同(因为异常不会起作用)。

[1]被称为Itanium C ++ ABI,在http://www.codesourcery.com/public/cxx-abi/abi.html上进行了记录。这些名称非常令人困惑:尽管ABI规范适用于包括i686 / x86_64在内的许多体系结构,但该名称是指原始的开发体系结构。GCC内部源代码和STL代码中的注释将Itanium称为“新” ABI,而之前使用的是“旧” ABI。更糟糕的是,“新” / Itanium ABI指的是通过;可获得的所有版本-fabi-version。“旧” ABI早于此版本。GCC在版本3.0中采用了Itanium / version /“新” ABI;如果我正确阅读了他们的变更日志,则在2.95及更早版本中使用了“旧” ABI。

[2]我找不到任何std::type_info平台列出的资源稳定性对象。对于我可以访问的编译器,我使用了以下命令:echo "#include <typeinfo>" | gcc -E -dM -x c++ -c - | grep GXX_MERGED_TYPEINFO_NAMES。从GCC 3.0开始,此宏控制GCC STL 中operator==for 的行为std::type_info。我确实发现mingw32-gcc遵循Windows C ++ ABI,其中std::type_info跨DLL的对象在类型上不是唯一的。在幕后typeid(a) == typeid(b)打来电话strcmp。我推测,在没有代码可链接的AVR等单程序嵌入式目标上,std::type_info对象始终是稳定的。


6
没有RTTI的例外情况。(您被允许抛出an int并且其中没有vtable :))
Billy ONeal 2014年

3
@Deduplicator:但是,当我在编译器中关闭RTTI时,它们工作得很好。抱歉让您失望。
比利·奥尼尔

5
异常处理机制必须能够满足满足一些基本要求的任何类型。您可以随意建议如何在不使用RTTI的情况下跨模块边界处理任意类型的引发和捕获异常。请考虑需要上下广播。
Deduplicator 2014年

15
typeid(a)== typeid(b)与B * ba = dynamic_cast <B *>(&a)不同。在派生类树上以随机级别对具有多重继承的对象进行尝试,您会发现typeid()== typeid()不会产生正数。dynamic_cast是在继承树中搜索实数的唯一方法。停止考虑通过禁用RTTI来节省成本,而仅使用它即可。如果容量过大,则可以优化代码膨胀。尽量避免在内部循环或任何其他对性能有要求的代码中使用dynamic_cast,您会很好的。
mysticcoder 2014年

3
@mcoder这就是为什么文章明确指出这一点的原因the latter necessarily involves traversing an inheritance tree plus comparisons。@CoryB不需要支持整个继承树的强制转换时,可以“负担得起”。例如,如果要在集合中查找所有类型为X的项,而不是从X派生的项,则应使用前者。如果还需要查找所有派生实例,则必须使用后者。
Aidiakapi 2014年

48

这些数字也许会有所帮助。

我正在使用此工具进行快速测试:

  • GCC Clock()+ XCode的探查器。
  • 1亿次循环迭代。
  • 2个2.66 GHz双核Intel Xeon。
  • 所讨论的类是从单个基类派生的。
  • typeid()。name()返回“ N12fastdelegate13FastDelegate1IivEE”

测试了5例:

1) dynamic_cast< FireType* >( mDelegate )
2) typeid( *iDelegate ) == typeid( *mDelegate )
3) typeid( *iDelegate ).name() == typeid( *mDelegate ).name()
4) &typeid( *iDelegate ) == &typeid( *mDelegate )
5) { 
       fastdelegate::FastDelegateBase *iDelegate;
       iDelegate = new fastdelegate::FastDelegate1< t1 >;
       typeid( *iDelegate ) == typeid( *mDelegate )
   }

5只是我的实际代码,因为在检查它是否类似于我已经拥有的对象之前,我需要创建该类型的对象。

没有优化

结果是(我平均进行了几次运行):

1)  1,840,000 Ticks (~2  Seconds) - dynamic_cast
2)    870,000 Ticks (~1  Second)  - typeid()
3)    890,000 Ticks (~1  Second)  - typeid().name()
4)    615,000 Ticks (~1  Second)  - &typeid()
5) 14,261,000 Ticks (~23 Seconds) - typeid() with extra variable allocations.

因此,结论是:

  • 对于未经优化的简单转换情况,typeid()速度是的两倍以上dyncamic_cast
  • 在现代机器上,两者之间的差异约为1纳秒(百万分之一毫秒)。

具有优化(-Os)

1)  1,356,000 Ticks - dynamic_cast
2)     76,000 Ticks - typeid()
3)     76,000 Ticks - typeid().name()
4)     75,000 Ticks - &typeid()
5)     75,000 Ticks - typeid() with extra variable allocations.

因此,结论是:

  • 对于具有优化效果的简单演员表,typeid()其速度比快近20倍dyncamic_cast

图表

在此处输入图片说明

代码

根据注释中的要求,代码在下面(有点混乱,但是可以使用)。可从此处获得“ FastDelegate.h” 。

#include <iostream>
#include "FastDelegate.h"
#include "cycle.h"
#include "time.h"

// Undefine for typeid checks
#define CAST

class ZoomManager
{
public:
    template < class Observer, class t1 >
    void Subscribe( void *aObj, void (Observer::*func )( t1 a1 ) )
    {
        mDelegate = new fastdelegate::FastDelegate1< t1 >;
        
        std::cout << "Subscribe\n";
        Fire( true );
    }
    
    template< class t1 >
    void Fire( t1 a1 )
    {
        fastdelegate::FastDelegateBase *iDelegate;
        iDelegate = new fastdelegate::FastDelegate1< t1 >;
        
        int t = 0;
        ticks start = getticks();
        
        clock_t iStart, iEnd;
        
        iStart = clock();
        
        typedef fastdelegate::FastDelegate1< t1 > FireType;
        
        for ( int i = 0; i < 100000000; i++ ) {
        
#ifdef CAST
                if ( dynamic_cast< FireType* >( mDelegate ) )
#else
                // Change this line for comparisons .name() and & comparisons
                if ( typeid( *iDelegate ) == typeid( *mDelegate ) )
#endif
                {
                    t++;
                } else {
                    t--;
                }
        }
        
        iEnd = clock();
        printf("Clock ticks: %i,\n", iEnd - iStart );
        
        std::cout << typeid( *mDelegate ).name()<<"\n";
        
        ticks end = getticks();
        double e = elapsed(start, end);
        std::cout << "Elasped: " << e;
    }
    
    template< class t1, class t2 >
    void Fire( t1 a1, t2 a2 )
    {
        std::cout << "Fire\n";
    }
    
    fastdelegate::FastDelegateBase *mDelegate;
};

class Scaler
{
public:
    Scaler( ZoomManager *aZoomManager ) :
        mZoomManager( aZoomManager ) { }
    
    void Sub()
    {
        mZoomManager->Subscribe( this, &Scaler::OnSizeChanged );
    }
    
    void OnSizeChanged( int X  )
    {
        std::cout << "Yey!\n";        
    }
private:
    ZoomManager *mZoomManager;
};

int main(int argc, const char * argv[])
{
    ZoomManager *iZoomManager = new ZoomManager();
    
    Scaler iScaler( iZoomManager );
    iScaler.Sub();
        
    delete iZoomManager;

    return 0;
}

1
当然,动态类型转换更为笼统-如果项目衍生性更高,则可以使用。例如,class a {}; class b : public a {}; class c : public b {};当目标的实例c类测试时将正常工作bdynamic_cast,但不能与typeid解决方案。仍然合理,但+1
Billy ONeal

34
该基准测试完全是虚伪的:typeid检查是循环不变的,并移出了循环。一点都不有趣,这是一个基本的基准测试。
恢复莫妮卡

3
@库巴:那么基准是虚假的。这不是关闭优化基准的理由;这就是编写更好的基准的原因。
Billy ONeal 2014年

3
再一次,这是一个失败。“对于带有优化的简单演员表,typeid()比dyncamic_cast快近20倍。” 他们不做同样的事情。有一个原因dynamic_cast较慢。
mysticcoder

1
@KubaOber:总计+1。这是如此经典。从周期数的角度看,这很明显。
v.oddou

38

这取决于事物的规模。在大多数情况下,它只是几次检查和一些指针取消引用。在大多数实现中,在具有虚拟功能的每个对象的顶部,都有一个指向vtable的指针,该表包含指向该类上虚拟功能的所有实现的指针列表。我想大多数实现都会使用它来存储指向该类的type_info结构的另一个指针。

例如在伪C ++中:

struct Base
{
    virtual ~Base() {}
};

struct Derived
{
    virtual ~Derived() {}
};


int main()
{
    Base *d = new Derived();
    const char *name = typeid(*d).name(); // C++ way

    // faked up way (this won't actually work, but gives an idea of what might be happening in some implementations).
    const vtable *vt = reinterpret_cast<vtable *>(d);
    type_info *ti = vt->typeinfo;
    const char *name = ProcessRawName(ti->name);       
}

通常,反对RTTI的真正理由是每次添加新的派生类时都必须在各处修改代码的不可维护性。不必在各处都使用switch语句,而是将其分解为虚函数。这会将类之间所有不同的所有代码移到类本身中,因此新的派生只需要覆盖所有虚函数即可成为一个功能齐全的类。如果您每次有人检查类的类型并执行不同的操作时都不得不遍历大型代码库,那么您将很快学会远离这种编程风格。

如果您的编译器允许您完全关闭RTTI,则最终的代码大小节省可能非常可观,而RAM空间却很小。编译器需要为每个具有虚函数的类生成type_info结构。如果关闭RTTI,则不需要在可执行映像中包含所有这些结构。


4
+1实际上解释了为什么使用RTTI被认为是一个糟糕的设计决定,这在我之前还不太清楚。
aguazales

6
这个答案是对C ++功能的低级理解。广泛使用的“一般”和“大多数实现”意味着您没有考虑如何很好地使用语言功能。虚拟功能和重新实现RTTI并不是解决方案。答案就是RTTI。有时您只想知道对象是否为某种类型。这就是为什么它在那里!因此,您会因某些type_info结构而损失几KB RAM。
e

16

好吧,探查器永远不会说谎。

由于我有一个非常稳定的18-20类型层次结构,并且变化不大,所以我想知道是否仅使用一个简单的枚举成员就能达到目的,并且避免了RTTI所谓的“高”成本。我怀疑RTTI是否实际上比if它引入的陈述要昂贵。男孩,男孩,是。

事实证明,RTTI 昂贵的,比C ++中的等效语句或简单的原始变量昂贵得多。因此,S.Lott的回答并不完全正确,RTTI 会产生额外的成本,这也不是因为只是声明中有所陈述。这是因为RTTI非常昂贵。ifswitchif

该测试是在Apple LLVM 5.0编译器上完成的,并启用了库存优化功能(默认发布模式设置)。

因此,我有以下2个功能,每个功能都可以通过1)RTTI或2)简单的开关来确定对象的具体类型。它这样做了50,000,000次。事不宜迟,我向您介绍了50,000,000次运行的相对运行时间。

在此处输入图片说明

没错,dynamicCasts占用了94%的运行时间。而该regularSwitch区块仅占3.3%

长话短说:如果您有能力enum像我在下文中介绍的那样插入'd类型,我可能会推荐它,如果您需要执行RTTI 并且性能至关重要。它只需要设置一次成员(确保通过所有构造函数获取它),并且一定不要在以后再编写它。

就是说,这样做不应该弄乱您的OOP实践。.仅在类型信息根本不可用并且您发现自己陷入使用RTTI的困境时才使用它。

#include <stdio.h>
#include <vector>
using namespace std;

enum AnimalClassTypeTag
{
  TypeAnimal=1,
  TypeCat=1<<2,TypeBigCat=1<<3,TypeDog=1<<4
} ;

struct Animal
{
  int typeTag ;// really AnimalClassTypeTag, but it will complain at the |= if
               // at the |='s if not int
  Animal() {
    typeTag=TypeAnimal; // start just base Animal.
    // subclass ctors will |= in other types
  }
  virtual ~Animal(){}//make it polymorphic too
} ;

struct Cat : public Animal
{
  Cat(){
    typeTag|=TypeCat; //bitwise OR in the type
  }
} ;

struct BigCat : public Cat
{
  BigCat(){
    typeTag|=TypeBigCat;
  }
} ;

struct Dog : public Animal
{
  Dog(){
    typeTag|=TypeDog;
  }
} ;

typedef unsigned long long ULONGLONG;

void dynamicCasts(vector<Animal*> &zoo, ULONGLONG tests)
{
  ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
  for( ULONGLONG i = 0 ; i < tests ; i++ )
  {
    for( Animal* an : zoo )
    {
      if( dynamic_cast<Dog*>( an ) )
        dogs++;
      else if( dynamic_cast<BigCat*>( an ) )
        bigcats++;
      else if( dynamic_cast<Cat*>( an ) )
        cats++;
      else //if( dynamic_cast<Animal*>( an ) )
        animals++;
    }
  }

  printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;

}

//*NOTE: I changed from switch to if/else if chain
void regularSwitch(vector<Animal*> &zoo, ULONGLONG tests)
{
  ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
  for( ULONGLONG i = 0 ; i < tests ; i++ )
  {
    for( Animal* an : zoo )
    {
      if( an->typeTag & TypeDog )
        dogs++;
      else if( an->typeTag & TypeBigCat )
        bigcats++;
      else if( an->typeTag & TypeCat )
        cats++;
      else
        animals++;
    }
  }
  printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;  

}

int main(int argc, const char * argv[])
{
  vector<Animal*> zoo ;

  zoo.push_back( new Animal ) ;
  zoo.push_back( new Cat ) ;
  zoo.push_back( new BigCat ) ;
  zoo.push_back( new Dog ) ;

  ULONGLONG tests=50000000;

  dynamicCasts( zoo, tests ) ;
  regularSwitch( zoo, tests ) ;
}

13

标准方式:

cout << (typeid(Base) == typeid(Derived)) << endl;

标准RTTI昂贵,因为它依赖于进行基础字符串比较,因此RTTI的速度可以根据类名长度而变化。

使用字符串比较的原因是使它跨库/ DLL边界一致地工作。如果您以静态方式构建应用程序和/或正在使用某些编译器,则可以使用:

cout << (typeid(Base).name() == typeid(Derived).name()) << endl;

不能保证能正常工作(永远不会产生假阳性,但可能会产生假阴性),但可以快15倍。这依赖于typeid()的实现以某种方式工作,并且您所做的只是比较内部char指针。有时也等同于:

cout << (&typeid(Base) == &typeid(Derived)) << endl;

但是,您可以安全地使用混合动力,如果类型匹配,混合动力将非常快,而对于不匹配的类型,混合动力将是最坏的情况:

cout << ( typeid(Base).name() == typeid(Derived).name() || 
          typeid(Base) == typeid(Derived) ) << endl;

要了解是否需要对此进行优化,您需要查看花费在获取新数据包上的时间与处理数据包所花费的时间相比。在大多数情况下,字符串比较可能不会有很大的开销。(取决于您的类或命名空间::类名的长度)

最安全的优化方法是将自己的typeid实现为int(或枚举Type:int)作为基础类的一部分,并使用该类型来确定类的类型,然后仅使用static_cast <>或reinterpret_cast < >

对我而言,未优化的MS VS 2005 C ++ SP1的差异大约是15倍。


2
“标准RTTI昂贵,因为它依赖于进行底层字符串比较”-不,与此无关的“标准”;这就是实现的typeid::operator工作方式。例如,受支持的平台上的GCC已使用char *s的比较,而无需我们强制执行-gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/…。当然,您的方式使MSVC的行为比您平台上的默认行为要好得多,因此声名狼藉,我不知道原生使用指针的“某些目标”是什么...但是我的意思是MSVC的行为绝不是“标准”。
underscore_d

7

对于简单的检查,RTTI可以和指针比较一样便宜。对于继承检查,strcmp如果您是继承人,则它的费用与继承树中每种类型的费用一样昂贵。dynamic_cast在一个实现中从上到下。

您也可以通过不使用来减少开销 dynamic_cast&typeid(...)==&typeid(type)显式检查类型来。尽管这不一定适用于.dll或其他动态加载的代码,但对于静态链接的事物而言,它可能会非常快。

尽管那时候就像使用switch语句,所以就可以了。


1
您对strcmp版本有任何参考吗?使用strcmp进行类型检查似乎效率极低且不准确。
JaredPar

在每个类型可能具有多个type_info对象的不良实现中,它可以将bool type_info :: operator ==(const type_info&x)const实现为“!strcmp(name(),x.name())”
Greg Rogers,

3
进入针对MSVC的dynamic_cast或typeid()。operator ==的反汇编,您将在其中找到一个strcmp。我认为这是在可怕的情况下,您正在与另一个.dll中编译的类型进行比较。而且它使用了错误的名称,因此至少对于相同的编译器来说是正确的。
MSN

1
您应该执行“ typeid(...)== typeid(type)”并且不比较地址
Johannes Schaub-litb

1
我的观点是,您可以尽早使用&typeid(...)==&typeid(blah),这很安全。由于typeid(...)可能在堆栈上生成,因此它实际上可能没有任何用处,但是如果它们的地址相等,则它们的类型相等。
MSN

6

最好总是进行测量。在下面的代码中,在g ++下,使用手工编码的类型标识似乎比RTTI快三倍。我敢肯定,使用字符串而不是char的更现实的手工编码实现会更慢,从而使时间紧迫。

#include <iostream>
using namespace std;

struct Base {
    virtual ~Base() {}
    virtual char Type() const = 0;
};

struct A : public Base {
    char Type() const {
        return 'A';
    }
};

struct B : public Base {;
    char Type() const {
        return 'B';
    }
};

int main() {
    Base * bp = new A;
    int n = 0;
    for ( int i = 0; i < 10000000; i++ ) {
#ifdef RTTI
        if ( A * a = dynamic_cast <A*> ( bp ) ) {
            n++;
        }
#else
        if ( bp->Type() == 'A' ) {
            A * a = static_cast <A*>(bp);
            n++;
        }
#endif
    }
    cout << n << endl;
}

1
尽量不要用dynamic_cast来做,而要用typeid来做。它可以提高性能。
Johannes Schaub-litb

1
但使用的dynamic_cast是比较现实的,至少在看我的代码

2
它做的是另一件事:它还检查bp是否指向从A派生的类型。您的=='A'检查它是否完全指向'A'。我还认为该测试在某种程度上是不公平的:编译器可以很容易地看到bp不能指向与A不同的任何东西,但是我认为它在这里没有优化。
Johannes Schaub-litb

无论如何,我已经测试了您的代码。对于RTTI,它的值为“ 0.016s”,对于虚拟函数调用的值为“ 0.044s”。(使用-O2)
Johannes Schaub-litb

尽管将其更改为使用typeid并没有什么区别(仍为0.016s)
Johannes Schaub-litb

4

不久前,我针对3GHz PowerPC在MSVC和GCC的特定情况下测量了RTTI的时间成本。在我运行的测试中(一个带有深层类树的相当大的C ++应用),每个测试的dynamic_cast<>成本在0.8μs到2μs之间,具体取决于它命中还是未命中。


2

那么,RTTI到底有多贵?

这完全取决于您使用的编译器。我了解有些使用字符串比较,有些使用实际算法。

您唯一的希望是编写一个示例程序,然后查看编译器的工作(或至少确定执行一百万dynamic_casts或一百万typeid秒所需的时间)。


1

RTTI可以便宜,并且不需要strcmp。编译器将测试限制为以相反的顺序执行实际的层次结构。因此,如果您有一个类C是类B的子类,而类B是子类A的子类,则从A * ptr到C * ptr的dynamic_cast意味着仅一个指针比较而不是两个指针(顺便说一句,只有vptr表指针是比较)。测试就像“如果(vptr_of_obj == vptr_of_C)返回(C *)obj”

另一个例子,如果我们尝试从A *到B *的dynamic_cast。在这种情况下,编译器将依次检查这两种情况(obj为C,而obj为B)。这也可以简化为单个测试(大多数情况下),因为虚拟函数表是作为汇总生成的,因此测试恢复为“ if(off(offset_of(vptr_of_obj,B)== vptr_of_B)”)

offset_of =返回sizeof(vptr_table)> = sizeof(vptr_of_B)?vptr_of_new_methods_in_B:0

的内存布局

vptr_of_C = [ vptr_of_A | vptr_of_new_methods_in_B | vptr_of_new_methods_in_C ]

编译器如何知道在编译时对此进行优化?

在编译时,编译器知道对象的当前层次结构,因此它拒绝编译不同类型的层次结构dynamic_casting。然后,它只需要处理层次结构的深度,并添加测试的反转量即可匹配该深度。

例如,这不会编译:

void * something = [...]; 
// Compile time error: Can't convert from something to MyClass, no hierarchy relation
MyClass * c = dynamic_cast<MyClass*>(something);  

-5

RTTI可能是“昂贵的”,因为您每次进行RTTI比较时都会添加一个if语句。在深度嵌套的迭代中,这可能会很昂贵。在某些永远不会循环执行的东西中,它实际上是免费的。

选择是使用适当的多态设计,消除if语句。在深度嵌套的循环中,这对于性能至关重要。否则,这并不重要。

RTTI也是昂贵的,因为它会使子类层次结构(即使有的子类层次结构)模糊不清。从“面向对象的编程”中删除“面向对象的”可能会有副作用。


2
不一定-我将通过dynamic_cast间接使用它,并保持层次结构不变,因为我需要向下转换,因为每个子类型都需要具有不同的(可变大小的)数据,并且必须以不同的方式应用,因此为dynamic_cast。
克里斯蒂安罗莫

1
@CristiánRomo:请用这些新事实更新您的问题。在C ++中,dynamic_cast是(有时)必需的工具。在被迫执行时询问RTTI性能并没有多大意义。
S.Lott

@ S.Lott:已更新。对不起,我很困惑。
克里斯蒂安罗莫

1
我刚刚对此做了一个实验 -事实证明,RTTI比if您以这种方式检查运行时类型信息时引入的语句要昂贵得多。
bobobobo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.