virtual

Virtuelle Funk­tionen werden erst zur Laufzeit, also nicht bereits zum Kom­pilierzeitpunkt, an eine Klasse ge­bunden. Das Programm ent­schei­det, welche Funktion für ein spezielles Objekt gewählt werden muss. Dadurch kann zur Laufzeit die richtige Version einer abgeleiteten Klasse aufgerufen werden. Man deklariert eine virtuelle Funktion in der Basisklasse. In abgeleiteten Klassen kann diese überschrieben wer­den. Entscheidend dabei ist, dass man mit einem Zeiger vom Typ der Basis­klas­se, der auf ein Objekt der ab­ge­lei­te­ten Klasse zeigt, auto­ma­tisch die richtige Methode, also die der ab­ge­lei­te­ten Klasse und nicht die der Basis­klasse, aufruft. Virtuelle Funk­tio­nen gehorchen somit dem Prinzip der „späten Bin­dung“. Für virtuelle Funktionen werden in C++ sogenannte V-Tables (virtual function table) verwendet.

 

Schauen wir uns an einigen Beispielen an, wie dieser wichtige Mecha­nis­mus funktioniert. Im nachfolgenden Programm verfügen wir über eine Basisklasse, zwei davon abgeleitete Kindklassen und zwei davon jeweils abgeleitete Enkelklassen. Alle Klassen besitzen die Funktion virt_function(),  die unter gleichem Namen "vielgestaltig" (polymorph) implementiert ist:

 

#include <iostream>

using namespace std;


class Basisklasse

{

  public: virtual void virt_function() { cout << "Basis sagt Hallo" << endl; };

};

 

class Kindklasse1 : public Basisklasse

{

  public: void virt_function() { cout << "Kind1 sagt Hallo" << endl; }

};

 

class Kindklasse2 : public Basisklasse

{

  public: void virt_function() { cout << "Kind2 sagt Hallo" << endl; }

};

 

class Enkelklasse1 : public Kindklasse1

{

  public: void virt_function() { cout << "Enkel1 sagt Hallo" << endl; }

};

 

class Enkelklasse2 : public Kindklasse2

{

  public: void virt_function() { cout << "Enkel2 sagt Hallo" << endl; }

};

 

int main()

{

    Basisklasse  basis;

    Kindklasse1  kind1;

    Kindklasse2  kind2;

    Enkelklasse1 enkel1;

    Enkelklasse2 enkel2;

      

    Basisklasse *pZeiger = &basis;

    pZeiger->virt_function();

   

    pZeiger = &kind1;

    pZeiger->virt_function();

 

    pZeiger = &kind2;

    pZeiger->virt_function();

 

    pZeiger = &enkel1;

    pZeiger->virt_function();

 

    pZeiger = &enkel2;

    pZeiger->virt_function();

 

    return 0;

}

 

In dem vorstehenden Programm wird für jedes Objekt die richtige Version der Funktion virt_function auf­ge­ru­fen. Bei virtuellen Funktionen entscheidet nicht der Typ des Zeigers, sondern der Typ des Objekts über die ver­wen­dete Funktion.  Damit klar wird, das dies durch den Bezeichner "virtual" ausgelöst wird, lassen wir ihn im nach­­fol­genden Beispiel versuchsweise weg:

 

class Basisklasse

{

  public:

     /*virtual*/ void virt_function() { cout << "Basis sagt Hallo" << endl; };

};

 

Nun wird für alle Objekte unabhängig vom Typ immer die Funktion der Basisklasse aufgerufen. Eine „späte Bin­dung“ in Ab­hän­gig­keit vom Objekttyp erfolgt nicht. Ohne virtuelle Funktion gelingt dies nur mit dem rich­ti­gen Objekttyp:

 

int main()

{

    ...

    basis.virt_function();

    kind1.virt_function();

    kind2.virt_function();

    enkel1.virt_function();

    enkel2.virt_function();

   

    return 0;

}

 

... oder mit Zeigern auf die jeweilige abgeleitete Klasse:

 

int main()

{

    ...

    (&basis)->virt_function();

    (&kind1)->virt_function();

    (&kind2)->virt_function();

    (&enkel1)->virt_function();

    (&enkel2)->virt_function();

      

    return 0;

}

 

Nachfolgend noch einige Hinweise zu virtuellen Funktionen:

Soll die virtuelle Funktion in einer abgeleiteten Klasse überschrieben, also neu definiert, werden, müssen Typ des Rückgabewertes und Zahl und Art der Parameter genau überein­stimmen.

Die Kennzeichnung „virtual“ kann in den abgeleiteten Klassen entfallen.

Nicht jede abgeleitete Klasse muss eine virtuelle Funktion redefinieren.


Übersicht Keywords C++