Der typeid-Operator

Mit Hilfe des typeid-Operators erhält man von C++ eine Referenz auf eine std::type_info-Struktur. Es kann sein, dass RTTI je nach Compiler noch mit einem Compilerflag aktiviert werden muss. Um die std::type_info-Struktur nutzen zu können, muss der C++-Header typeinfo includiert werden.

#include <typeinfo>

Typen vergleichen

Die type_info-Struktur ist für alle Objekte eines Typs identisch, wie es auch die vTable ist. Somit kann man Typen von Variablen miteinander oder mit expliziten Typen vergleichen:

statisch Typen vergleichen (ohne vTable)

#include <typeinfo>
#include <stdio.h>
#include <stdlib.h>
 
class Farbe    // Farbe hat keine virtuellen Member
{
public:
 
  int rgbWert;
};
 
class FarbeMitAlphaWert : public Farbe
{
public:
  int Alpha;
};
 
void test( Farbe & lhs, Farbe & rhs )
{
  if( typeid( lhs ) == typeid( rhs ) ) 
    printf( "lhs hat den gleichen Typ wie rhs\n" );
  else
    printf( "lhs hat einen anderen Typ als rhs\n" );
}
 
int main( void )
{
  Farbe       farbe1;
  Farbe       farbe2;
  Farbe const farbeConst = farbe2; 
 
  if( typeid( farbe1 ) == typeid( farbe2 ) ) 
    printf( "farbe1 hat den gleichen Typ wie Farbe2\n" );
 
  if( typeid( farbe1 ) == typeid( farbeConst ) ) 
    printf( "farbe1 hat den gleichen Typ wie FarbeConst\n" );
 
  if( typeid( farbe1 ) == typeid( int ) )
    printf( "farbe1 ist ein int\n" );
 
  if( typeid( farbe1 ) == typeid( int ) )
    printf( "farbe1 ist ein int\n" );
  else
    printf( "farbe1 ist KEIN int\n" );
 
// Ab hier wird es ernst
  printf( "--------------\n" );
 
  printf( "Groeße einer Farbe: %d\n", sizeof( Farbe ));
 
  FarbeMitAlphaWert alpha;
 
  printf( "test( farbe1, farbe2 ): " );
  test( farbe1, farbe2 );
 
  printf( "test( farbe1, alpha ): " );
  test( farbe1, alpha );
 
  return EXIT_SUCCESS; 
}

Das oben stehende Programm kann nur statisch zur Laufzeit prüfen. Hier das Ergebnis:

Neo:rtti xin$ ./a.out 
farbe1 hat den gleichen Typ wie Farbe2
farbe1 hat den gleichen Typ wie FarbeConst
farbe1 ist KEIN int
--------------
Groeße einer Farbe: 4
test( farbe1, farbe2 ): lhs hat den gleichen Typ wie rhs
test( farbe1, alpha ): lhs hat den gleichen Typ wie rhs

Während die oberen Tests wunderbar funktionieren, testet die Funktion test ebenfalls statisch auf den Typ Farbe. Und da Farbe keine virtuellen Methoden kennt, besitzt es keine Objekttyp abhängige RTTI-Information. Eine Farbe mit AlphaWert ist also im Kontext der Funktion test auch nur eine Farbe.

Es kann Compiler geben, bei denen der Text „farbe1 hat den gleichen Typ wie FarbeConst“ nicht erscheint, da sie die Attribute const und volatile als eigene Typen darstellen. Vom Standard her sollte nur der Typ unabhängig von den Attributen verglichen werden.

statisch Typen vergleichen (ohne vTable)

Sobald eine vTable vorhanden ist, unterscheidet sich diese bei jedem Objekttypen. Hierfür geben wir der Klasse Farbe einfach einen überschreibbaren Destruktor mit:

class Farbe    // Farbe hat nun einen virtuellen Destruktor
{
public:
 
  int rgbWert;
 
  virtual ~Farbe() {} 
};

Entsprechend ändert sich die Ausgabe unseres Testprogramms:

farbe1 hat den gleichen Typ wie Farbe2
farbe1 hat den gleichen Typ wie FarbeConst
farbe1 ist KEIN int
--------------
Groeße einer Farbe: 8
test( farbe1, farbe2 ): lhs hat den gleichen Typ wie rhs
test( farbe1, alpha ): lhs hat einen anderen Typ als rhs

Die Farbe ist um einige Bytes gewachsen: dies ist der Zeiger auf die vTable. Durch die Tatsache, dass jedes Objekt nun einen Zeiger auf die eigene Objektbeschreibung mit sich führt, können die Objekttypen nun korrekt verglichen werden. Eine Farbe unterscheidet sich nun von einer FarbeMitAlphaWert.

Den Namen eines Typs ausgeben

Gerade bei der Fehlersuche ist es manchmal sehr hilfreich zu erfahren, welcher Datentyp soeben in der Funktion verwendet wird. Folgendes kleines Testprogramm holt sich den Namen der Klasse aus der std::type_info-Struktur und gibt ihn aus.

#include <typeinfo>
#include <stdio.h>
#include <stdlib.h>
 
class Farbe
{
public:
  int rgbWert;
 
  virtual ~Farbe() {}
};
 
class FarbeMitAlphaWert : public Farbe
{
public:
  int Alpha;
};
 
void printName( Farbe & type )
{
  printf( "%s\n", typeid( type ).name() );
}
 
int main( void )
{
  Farbe             farbe;
 
  FarbeMitAlphaWert alpha;
 
  printf( "farbe : " );
  printName( farbe );
 
  printf( "alpha : " );
  printName( alpha );
 
  return EXIT_SUCCESS; 
 
}

Wie der hier verwendete GCC-Compiler zeigt, muss der Klassenname nicht zwangsläufig identisch mit dem Klassennamen im Quelltext sein:

farbe : 5Farbe
alpha : 17FarbeMitAlphaWert