Vererbung und Polymorphie
-
Hallo,
runtime polymorphism geht in C++ nur über Zeiger.
Deine vector könnte so aussehen:std::vector<std::unique_ptr<foo>> vec;Ausserdem müssen virtuelle Funktion als soche gekennzeichnet werden ('virtual').
-
Ich habs jetzt wie folgt probiert
#include <iostream> #include <vector> #include <memory> class foo { public: virtual void p() { std::cout << "foo" << std::endl; } }; template<class T> class bar : public foo { public: bar(T _data) : data{_data} {} void p() override { std::cout << "bar" << std::endl; } private: T data; }; int main() { std::vector<std::unique_ptr<foo>> vec; bar<int> b{7}; std::unique_ptr<foo> p( &b ); vec.push_back(p); for(std::unique_ptr<foo> f : vec) { f->p(); } }Dabei bekomme ich aber die kryptische Fehlermeldung
[Error] use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = foo; _Dp = std::default_delete<foo>]'
-
Hallo,
unique_ptr sínd 'unique' und dürfen nicht kopiert werden.
Grob könnte das so aussehen:std::vector<std::unique_ptr<foo>> vec; auto uptr = std::unique_ptr<foo>(new bar<int>{7}); vec.push_back(std::move(uptr)); // move statt copy for(auto& f : vec) // & statt copy { f->p(); }Edit: Gut, dass Java-Mensch nicht ständig 'new' verwendest.
Das 'new' hier kannst du auch durch make_unique ersetzen, sofern dein Kompiler das kennt.
---
Dein Einsparversuch bzgl. new ist zwar löblich, hier aber falsch.
Sowohl der unique_ptr, als auch das Object selber beim verlassen des scopes, würden sich zerstören wollen.
-
Vielen Dank. Jetzt habe ich es verstanden.
Anbei noch einmal das ganze Beispiel, falls noch mal jemand anderes das gleiche Problem hat
#include <iostream> #include <memory> #include <vector> // Abstrakte Basisklasse class BaseClass { public: virtual void output() = 0; }; // Abgeleitete Klasse template<class T> class Derived : public BaseClass { public: Derived(T _data) : data{_data} {} virtual void output() { std::cout << data << std::endl; } private: T data; }; int main() { std::vector<std::unique_ptr<BaseClass>> vector; auto myObjectPointer = std::unique_ptr<BaseClass>(new Derived<int>{7}); vector.push_back(std::move(myObjectPointer)); for(auto& obj : vector) { obj->output(); } return 0; }
-
Nur der Vollständigkeit halber:
BaseClassbraucht noch einen virtuellen Destruktor:
-
Warum ? Ich dachte nen eigenen Destruktor brauch ich nur wenn ich Speicher alloizierte, verhält sich das bei Vererbung dann anders?
-
Ja, denn wenn du nur einen Basisklassenzeiger hast und dadrüber dein Objekt löscht, dann wird bei einem Nicht-Virtuellen Destruktor nur der Basisklassenteil deines Objektes gelöscht. Denn der Compiler denkt quasi, dass es da nicht mehr zum löschen gibt.
Umgekehrt ist es natürlich so wie du sagst. Du brauchst ja eigentlich keinen Destruktor. Scott Meyers empfiehlt daher folgendes:
class Base { // ... public: virtual ~Base() = default; };(Und das bei Bedarf sogar in die Implementationsdatei ausgelagert)
-
Ok, Danke!

-
Jockelx schrieb:
Hallo,
runtime polymorphism geht in C++ nur über Zeiger.
Über Referenzen geht es auch (in Java sind ja kompliziertere Datentypen auch nur Referenzen, in C++ muss man Referenzen halt als solche kennzeichnen, weil es auch noch Pointer gibt).
In Deinem Beispiel in Java sind sowohl b als auch f beides Referenzen. In C++ ist f tatsächlich ein Objekt vom Typ foo (dieses wird initialisiert mit einem Objekt vom Typ bar - alle Datenelemente von bar, die es auch in foo gibt werden mit den Werten von b initialisiert) und der Typ eines Objekts kann nachträglich nicht mehr geändert werden, weshalb die Funktion p von foo aufgerufen wird. In Java ist dagegen "foo f = b;" eine Referenz f die auf ein Objekt vom Typ bar zeigt (wie in meinem C++-Beispiel unten) wodurch auch p von bar aufgerufen wird.
Habe das Ursprungsbeispiel mal an 2 Stellen (virtual und foo &f ... statt foo f ...) geändert - gibt dann das gleiche aus wie Dein Java-Beispiel:
#include <iostream> class foo { public: virtual void p() { std::cout << "foo" << std::endl; } }; class bar : public foo { public: void p() { std::cout << "bar" << std::endl; } }; int main() { bar b; foo &f = b; f.p(); return 0; }Allerdings kann man in C++ nachträglich einer Referenz kein anderes Objekt mehr zuweisen. Der Name der Referenz ist gleichberechtigt wie der Name des Objekts selbst.
Also wenn z. B. nach dem
foo &f = b;
später irgendwo ein
foo f2;
f = f2;" // ist das gleiche wie b=f2, da f auf das Objekt b vom Typ bar zeigtkommt, ist f immer noch eine Referenz auf das Objekt b vom Typ bar und somit werden b die Datenelemente von f2 zugewiesen - was mit den Datenelementen passiert, die es in dem abgeleiteten Typ b nicht gibt, weiß ich nicht (ändern sich vermutlich nicht - kann man ja im Compiler ausprobieren). In Java bin ich mir da jetzt nicht ganz sicher, ob f dann eine Referenz auf f2 oder b ist (ich glaube es ist dann eine Referenz auf f2 - so ein Verhalten kann man in C++ nur mit einem Pointer erzielen - EDIT: und selbst dann muss man noch einen [gefährlichen] reinterpret_cast verwenden).
WICHTIG: f ist tatsächlich vom Typ foo auch wenn es auf ein abgeleitetes Objekt bar zeigt (alle Datenelemente und Methoden von foo sind auch in bar enthalten, weshalb das zulässig ist - umgekehrt nicht). Das merkt man, wenn man in bar eine weitere Methode "q() {std::cout<<"no method of foo"<<endl;}" einfügt und dann in main die Zeile:
f.q();
ergänzt. Dann bringt der Compiler:
foobar.cpp(30) : error C2039: 'q' : is not a member of 'foo'
foobar.cpp(4) : see declaration of 'foo'
-
johan schrieb:
Jockelx schrieb:
Hallo,
runtime polymorphism geht in C++ nur über Zeiger.
Über Referenzen geht es auch (in Java sind ja kompliziertere Datentypen auch nur Referenzen, in C++ muss man Referenzen halt als solche kennzeichnen, weil es auch noch Pointer gibt).
Ja, hab mich gestern in gleich 2 Threads unglücklich ausgedrückt.
Verbesserungsvorschlag:Container, die Elemente beinhalten welche runtime polymorphism unterstützen sollen, sollten idR (smart-)Pointer benutzen.
Nebenbei ist eine Java-'Reference' meiner Meinung nach eher mit einem C++-Pointer als mit einer C++-Reference vergleichbar. Aber das gehört hier nicht hin.