Sinn von Virtual
-
Guten Abend,
ich versuche glaube ich nun bald seit einem Monat den Sinn von "virtual" zu verstehen in der Polymorphie von C++. Ich weiß das man wenn man in der Basisklasse vor einer Methode "virtual" schreibt, dass sie in einer Unterklasse "überschrieben" werden kann. Ich meine, im folgenden Beispiel geht eine Überschreibung auch ohne virtual.#include <iostream> class Basis { public: void Methode() { std::cout << "Moin"; } }; class Unter : public Basis { public: void Methode() { std::cout << "Moin2"; } }; int main() { Basis b; b.Methode(); Unter u; u.Methode(); }Ich habe schon mehrere Tutorials gelesen und versucht zu verstehen, von daher bitte keine Links die ich mit 90% Sicherheit schon gelesen habe. Vielleicht kann es mir jemand bitte kurz und bündig und vielleicht an Hand eines guten Beispiels klarer machen.
Mit freundlichen Grüßen : FreakY
-
Unter u; Basis *p = &u; p->Methode();Ohne virtual würde hier Basis::Methode() aufgerufen, obwohl wir es ja mit einem Objekt der Klasse "Unter" zu tun haben. Mit virtual wird korrekt Unter::Methode() aufgerufen. Überschreiben ist zwar auch ohne virtual möglich, führt aber selten zum gewünschten Ergebnis.
-
#include <iostream> using namespace std; class A { public: A(){} ~A(){} void foo() { cout << "a" << endl; } }; class B : public A { public: B(){} ~B(){} void foo() { cout << "b" << endl; } }; int main() { B b; A& a(b); a.foo(); }Ausgabe: a
#include <iostream> using namespace std; class A { public: A(){} virtual ~A(){} virtual void foo() { cout << "a" << endl; } }; class B : public A { public: B(){} ~B(){} void foo() { cout << "b" << endl; } }; int main() { B b; A& a(b); a.foo(); }Ausgabe: b
-
Hm okay vielen dank, ich glaube mit euren Beispielen habe ich einen besseren Einblick bekommen.
-
Um verwirrung mal vorzubeugen. Bei der Vererbung gibt es auch ein virtual.
class a {}; class b : virtual a{}; // virtuelle vererbung class c : virtual a{}; // virtuelle vererbung class d : public b , public c {}; // c erbt nur von einem bhttp://en.wikipedia.org/wiki/Diamond_problem
Hat aber rein gar nichts mit dem zutun, was du gerade machst. Einfach nur, dass du später nicht verwirrt bist, wenn du virtuelle vererbung siehst.

-
Das Vorteil ist, dass Du ein einheitliches Interface für Tätigkeiten hast. Als Beispiel nehme ich mal das Öffnen von Behältern. Man kann z.B. Flaschen, Kisten etc. öffnen. Das geht aber immer unterschiedlich (aufschrauben, aufklappen usw.). Über virtuelle Funktionen kannst Du den verschienden Behältern eine open-Methode zur Verfügung stellen, die intern das vom jeweiligen Container benötigte tut. Nach außen hin musst Du aber einfach nur open() aufrufen.
Beispiel:struct OpenableContainer { virtual void open() = 0; virtual ~OpenableContainer(){} }; class Bottle : public OpenableContainer { //irgendwelcher Bottle-Code void open() { //code zum oeffnen von Flaschen } }; class Box : public OpenableContainer { //irgendwelcher Box-Code void open() { //code zum oeffnen von Kisten } }; class Footlocker : public OpenableContainer { //irgendwelcher Footlocker-Code void open() { //code zum oeffnen von ...Kisten } }; std::vector<OpenableContainer> containers; containers.push_back(Bottle()); //fuegt eine Buddel ein containers.push_back(Box()); //und eine Kiste containers.push_back(Footlocker()); //und noch eine void open(const std::vector<OpenableContainer>& containers) { //alle Behaelter im Vektor oeffnen for(size_t n = 0; n != containers.size(); ++n) { containers.[i].open(); //hier werden die virtuellen Methoden aufgerufen. Und zwar immer die passenden } }
-
Tachyon schrieb:
Beispiel:
std::vector<OpenableContainer> containers; containers.push_back(Bottle()); //fuegt eine Buddel ein containers.push_back(Box()); //und eine Kiste containers.push_back(Footlocker()); //und noch eineUnd genau so geht es nicht. Du kannst schon mal keinen std::vector einer pure virtuell class erzeugen. Und der push_back würde, wenn die abgeleitete Klasse dann mit dem Kopierkonstruktor in die Basisklasse wandeln. Damit wäre die virtuelle Methode auch hinfällig.
Virtuelle Methoden machen nur Sinn, wenn Zeiger oder Referenzen auf Basisklassen verwendet werden, die dann auf Instanzen abgeleiteter Klassen verweisen. Also etwa so:
std::vector<OpenableContainer*> containers; containers.push_back(new Bottle()); //fuegt eine Buddel ein containers.push_back(new Box()); //und eine Kiste containers.push_back(new Footlocker()); //und noch eineWobei hier der Entwickler für die Aufräumarbeiten mittels delete zuständig ist.
-
Jo, stimmt. Hab net ans Slicing gedacht.
-
tntnet schrieb:
Wobei hier der Entwickler für die Aufräumarbeiten mittels delete zuständig ist.
Um das zu umgehen, kann man beispielsweise Boost.PointerContainer nutzen, da wird automatisch aufgeräumt. Oder man setzt Smart Pointers in Standardcontainern ein.