Ich habe meine Antwort in Form eines Gesprächs, um sie besser lesen zu können:
Warum brauchen wir virtuelle Funktionen?
Wegen des Polymorphismus.
Was ist Polymorphismus?
Die Tatsache, dass ein Basiszeiger auch auf abgeleitete Typobjekte verweisen kann.
Wie führt diese Definition des Polymorphismus zur Notwendigkeit virtueller Funktionen?
Nun, durch frühes Binden .
Was ist frühe Bindung?
Frühe Bindung (Bindung zur Kompilierungszeit) in C ++ bedeutet, dass ein Funktionsaufruf behoben wird, bevor das Programm ausgeführt wird.
Damit...?
Wenn Sie also einen Basistyp als Parameter einer Funktion verwenden, erkennt der Compiler nur die Basisschnittstelle. Wenn Sie diese Funktion mit Argumenten aus abgeleiteten Klassen aufrufen, wird sie abgeschnitten, was nicht der Fall ist.
Wenn es nicht das ist, was wir wollen, warum ist das erlaubt?
Weil wir Polymorphismus brauchen!
Was ist dann der Vorteil des Polymorphismus?
Sie können einen Basistypzeiger als Parameter einer einzelnen Funktion verwenden und dann zur Laufzeit Ihres Programms ohne Probleme auf jede der abgeleiteten Typschnittstellen (z. B. deren Elementfunktionen) zugreifen, indem Sie die Dereferenzierung dieser einzelnen Funktion verwenden Basiszeiger.
Ich weiß immer noch nicht, wofür virtuelle Funktionen gut sind ...! Und das war meine erste Frage!
Das liegt daran, dass Sie Ihre Frage zu früh gestellt haben!
Warum brauchen wir virtuelle Funktionen?
Angenommen, Sie haben eine Funktion mit einem Basiszeiger aufgerufen, der die Adresse eines Objekts aus einer seiner abgeleiteten Klassen hatte. Wie wir oben bereits erwähnt haben, wird dieser Zeiger zur Laufzeit dereferenziert. Bisher so gut, dass wir jedoch erwarten, dass eine Methode (== eine Mitgliedsfunktion) "von unserer abgeleiteten Klasse" ausgeführt wird! In der Basisklasse ist jedoch bereits dieselbe Methode definiert (eine mit demselben Header). Warum sollte sich Ihr Programm also die Mühe machen, die andere Methode auszuwählen? Mit anderen Worten, ich meine, wie können Sie dieses Szenario von dem unterscheiden, was wir früher normalerweise gesehen haben?
Die kurze Antwort lautet "eine virtuelle Mitgliedsfunktion in der Basis", und eine etwas längere Antwort lautet: "Wenn das Programm in diesem Schritt eine virtuelle Funktion in der Basisklasse sieht, weiß es (erkennt), dass Sie versuchen, sie zu verwenden Polymorphismus "und geht so zu abgeleiteten Klassen (unter Verwendung von v-table , einer Form der späten Bindung), um herauszufinden, dass eine andere Methode mit demselben Header, aber mit - erwartungsgemäß - einer anderen Implementierung.
Warum eine andere Implementierung?
Du Knöchelkopf! Geh und lies ein gutes Buch !
OK, warte, warte, warte, warum sollte man sich die Mühe machen, Basiszeiger zu verwenden, wenn man einfach abgeleitete Typzeiger verwenden könnte? Sie sind der Richter, sind all diese Kopfschmerzen es wert? Schauen Sie sich diese beiden Schnipsel an:
// 1:
Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();
// 2:
Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();
OK, obwohl ich denke, dass 1 immer noch besser als 2 ist , könnten Sie 1 auch so schreiben :
// 1:
Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();
Darüber hinaus sollten Sie sich darüber im Klaren sein, dass dies nur eine erfundene Verwendung all der Dinge ist, die ich Ihnen bisher erklärt habe. Nehmen Sie stattdessen beispielsweise eine Situation an, in der Sie eine Funktion in Ihrem Programm hatten, die die Methoden aus jeder der abgeleiteten Klassen verwendete (getMonthBenefit ()):
double totalMonthBenefit = 0;
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
totalMonthBenefit += x -> getMonthBenefit();
}
Versuchen Sie nun, dies ohne Kopfschmerzen neu zu schreiben !
double totalMonthBenefit=0;
Shop1* branch1 = &shop1;
Shop2* branch2 = &shop2;
Shop3* branch3 = &shop3;
Shop4* branch4 = &shop4;
Shop5* branch5 = &shop5;
Shop6* branch6 = &shop6;
totalMonthBenefit += branch1 -> getMonthBenefit();
totalMonthBenefit += branch2 -> getMonthBenefit();
totalMonthBenefit += branch3 -> getMonthBenefit();
totalMonthBenefit += branch4 -> getMonthBenefit();
totalMonthBenefit += branch5 -> getMonthBenefit();
totalMonthBenefit += branch6 -> getMonthBenefit();
Und tatsächlich könnte dies auch noch ein erfundenes Beispiel sein!