C++-Programmierung/ Objektorientierte Programmierung/ Virtuelle Methoden
Einleitung
BearbeitenIn diesem Abschnitt geht es um virtuelle Methoden. Dies sind Methoden, bei denen man erwartet, dass sie in abgeleiteten Klassen redefiniert werden. Der Hauptnutzen von virtuellen Methoden ist die korrekte Verwendung von gleichnamigen Mitgliedsmethoden in einer Vererbungshierarchie. So kann bewirkt werden, dass die Verwendung eines Basisklassenzeigers oder einer Basisklassenreferenz bei Aufruf die Methode am Ende der Hierarchie aufruft, ohne dass diese an der jeweiligen Stelle bekannt sein muss. So wird erreicht, dass das Objekt nicht in einen ungültigen Zustand gerät, wenn eine Instanz einer ggf. mehrfach abgeleiteten Klasse über ihre Basisklassenschnittstelle angesprochen wird.
Was bringen sie?
BearbeitenVirtuelle Methoden ermöglichen es dem Übersetzer, die passendste Methode in der Klassenhierarchie zu finden. Wird auf dieses reservierte Wort verzichtet, so wird im Zweifelsfall immer die Methode mit der gleichen Signatur des Urahnen genommen.
Virtuelle Methoden gibt es nicht in allen objektorientierten Sprachen. So entspricht sie in Java einer ganz normalen Methode, die nicht final
ist.
#include <iostream>
class Tier{
public:
virtual void iss() { std::cout << "Fresse wie ein Tier" << std::endl; };
// void iss() { std::cout << "Fresse wie ein Tier" << std::endl; };
};
class Hund : public Tier{
public:
void iss() { std::cout << "Wuff! Fresse gerade" << std::endl; };
};
class Mensch : public Tier{
public:
void iss() { std::cout << "Esse gerade" << std::endl; };
};
#include <vector>
int main(){
std::vector<Tier*> tiere;
tiere.push_back(new Tier());
tiere.push_back(new Hund());
tiere.push_back(new Mensch());
for (std::vector<Tier*>::const_iterator it = tiere.begin(); it != tiere.end(); it++){
(*it)->iss();
delete *it;
}
return 0;
}
Fresse wie ein Tier
Wuff! Fresse gerade
Esse gerade
Würden wir im obigen Beispiel das virtual
entfernen, so hätten wir das Ergebnis
Virtuelle Destruktoren
BearbeitenEine weitere Eigenheit von C++ sind Destruktoren, die für Tätigkeiten wie Speicherfreigabe verwendet werden. Jede Klasse, deren Attribute nicht primitive Typen sind oder die andere Ressourcen verwendet (wie z.B. eine Datenbankverbindung) sollten diese unbedingt in ihren Destruktoren freigeben. Um auf den richtigen Destruktor immer zugreifen zu können, muss der Destruktor des Urahnen mit dem Wort virtual
beginnen.
Folgendes Beispiel zeigt die Verwendung und Vererbung von nicht-virtuellen Destruktoren, was zu undefiniertem Verhalten führt.
#include <iostream>
class A{
public:
A() { }
~A() { std::cout << "Zerstöre A" << std::endl; }
};
class B : public A{
public:
B() { }
~B() { std::cout << "Zerstöre B" << std::endl; }
};
int main() {
A* b1 = new B;
B* b2 = new B;
delete b1; // Gemäß C++-Standard undefiniertes Verhalten.
// Meist wird nur ~A() aufgerufen, da ~A() nicht virtuell.
delete b2; // Destruktoren ~B() und ~A() werden aufgerufen
return 0;
}
Zerstöre A
Zerstöre B
Zerstöre A