Bene, il profiler non mente mai.
Dal momento che ho una gerarchia piuttosto stabile di 18-20 tipi che non sta cambiando molto, mi chiedevo se il solo uso di un semplice membro enum avrebbe fatto il trucco ed evitasse il presunto "alto" costo di RTTI. Ero scettico se RTTI fosse effettivamente più costoso della semplice if
dichiarazione che introduce. Ragazzo oh ragazzo, vero?
Si scopre che RTTI è costoso, molto più costoso di un'istruzione equivalente if
o di una semplice switch
su una variabile primitiva in C ++. Quindi la risposta di S.Lott non è del tutto corretta, ci sono costi extra per RTTI e non è dovuto al fatto che if
hai solo una dichiarazione nel mix. È dovuto al fatto che RTTI è molto costoso.
Questo test è stato eseguito sul compilatore Apple LLVM 5.0, con le ottimizzazioni di magazzino attivate (impostazioni della modalità di rilascio predefinita).
Quindi, ho sotto 2 funzioni, ognuna delle quali determina il tipo concreto di un oggetto tramite 1) RTTI o 2) un semplice interruttore. Lo fa 50.000.000 di volte. Senza ulteriori indugi, vi presento i relativi tempi di funzionamento per 50.000.000 di corse.
Proprio così, l' dynamicCasts
ha preso il 94% del tempo di esecuzione. Mentre il regularSwitch
blocco ha preso solo il 3,3% .
Per farla breve: se puoi permetterti di agganciare un enum
tipo di tipo come ho fatto sotto, probabilmente lo consiglierei, se hai bisogno di fare RTTI e le prestazioni sono di primaria importanza. Ci vuole solo una volta l'impostazione del membro (assicurarsi di ottenerlo tramite tutti i costruttori ) e assicurarsi di non scriverlo mai dopo.
Detto questo, fare ciò non dovrebbe incasinare le tue pratiche OOP .. è pensato solo per essere usato quando le informazioni sul tipo semplicemente non sono disponibili e ti trovi ad affrontare l'utilizzazione di 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 ) ;
}