Überschreiben von Funktionen

Im Kapitel über die allgemeinen Spielregeln zum Thema ableiten haben wir Gemeinsamkeiten von Objekten zusammengefasst und so eine Oberklasse dieser Objekte geschaffen. Anschließend haben wir Spezialisierungen kennen gelernt: Hier noch einmal der Quelltext dazu:

class Tier
{
public:
  char Name[64];
  int  Fellfarbe;
  int  KrallenLaenge;
  int  AnzahlBeine;
 
  inline void GibNamenAus( void ) { printf( "Ich heiße %s\n", Name ); }
};
 
class Hund : public Tier
{
public:
  int  GefangeneKatzen;
 
  inline void GibLaut( void ) { printf( "Wau!\n" ); }
};
 
class Katze : public Tier
{
public:
  int   GefangeneVoegel;
 
  inline void GibLaut( void ) { printf( "Miau!\n" ); }
};
 
class Amsel : public Tier
{
public:
  int  GefangeneInsekten;
 
  inline void GibLaut( void ) { printf( "Piep!\n" ); }
};

Wir sehen, dass alle Tiere eine Funktion GibLaut kennen, aber das Tier selbst nicht. Solange wir wissen, um welches Tier es sich handelt können wir also GibLaut() rufen. Wir erweitern das Tier nun so, dass es ebenfalls einen Laut abgeben kann. Da wir nicht wissen, um welches Tier es sich handelt, müssen wir die Funktion so schreiben, dass sie für alle Tiere passt:

class Tier
{
public:
  char Name[64];
  int  Fellfarbe;
  int  KrallenLaenge;
  int  AnzahlBeine;
 
  inline void GibNamenAus( void ) { printf( "Ich heiße %s\n", Name ); }
  inline void GibLaut( void ) { printf( "< - unverständliches Geräuch - >\n" ); }
};
 
class Hund : public Tier
{
public:
  int  GefangeneKatzen;
 
  inline void GibLaut( void ) { printf( "Wau!\n" ); }
};

Exemplarisch habe ich den Hund mitkopiert. Die Methode GibLaut() gibt es nun im Tier und identisch gibt es sie erneut im Hund (wie auch in Katze, Amsel). Damit wird für den Hund (…) die Methode, die in Tier beschreiben ist, unsichtbar. Man sagt, die Methode wurde überschrieben.

Es handelt sich um eine statische Überschreibung, da die Überschreibung statisch vom verwendeten Datentyp der Variablen abhängt. Das sei an folgendem Beispiel gezeigt:

Hund * bello = new Hund();
Tier * tier = bello;

bello->GibLaut();
tier->GibLaut();

tier zeigt auf den gleichen Hund, wie bello, aber bello ist vom Datentyp Hund, während tier vom Datentyp Tier ist. Da die Funktion statisch gebunden ist, wird im einen Fall die Methode von Hund gerufen (Hund::GibLaut()) und im anderen Fall Tier::GibLaut().

Aus Ausgabe kommt hiermit also:

Wau!
< - unverständliches Geräuch - >

Methoden von Basisklassen rufen

Natürlich verliert ein Hund keine Fähigkeiten, die er aus seiner Oberklasse erbt. bello kann sich also weiterhin wie ein Tier verhalten, dafür muss man ihn nur dazu auffordern. Dies funktioniert genauso wie bei der Konstruktion eines Objektes, dass den Basiskonstruktor ruft:

Hund * bello = new Hund();
 
bello->GibLaut();        // Standardverhalten   : hier virtuell gebundene Methode Hund::GibLaut()
bello->Hund::GibLaut();  // Explizites Verhalten: hier statisch gebundene Methode Hund::GibLaut()
bello->Tier::GibLaut();  // Explizites Verhalten: hier statisch gebundene Methode Tier::GibLaut()

Die Ausgabe ist entsprechend:

Wau!
Wau!
< - unverständliches Geräuch - >

Achtung: auch wenn hier statische gebundene Methoden überschrieben werden, so besteht ein Unterschied zu statischen Methoden (Methoden im Namensraum der Klasse, die als static deklariert sind). Statisch gebundene Methoden sind an den Datentyp gebunden, erhalten eine Instanz des Objektes als this-Zeiger übergeben. In diesem Fall die Instanz von Hund. Eine statische Methode ist unabhängig von einer Instanz der Klasse, hier gibt es entsprechend keinen this-Zeiger, es handelt sich um eine ganz normale Funktion, die sich im Namensraum der Klasse befindet.