Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält
-
Hallo,
ich habe eine BasisklasseX
und jedes Objekt, das von der BasisklasseY
erbt, erbt auch von der KlasseX
.Y
undZ
sind Klassen aus einer externen Bibliothek. . Wenn ich aber einstd::array
deklariere, was soll der Typ sein? Wenn ich einen der beiden Typen als Typ angebe, so werden Methodenaufrufe danach als ungültig erklärt, weil ein der der KlasseX
zugehörige Objekt natürlich keine Methoden der KlasseY
ausführen kann. Außerdem kann ich keine Klasse erstellen, die beide Klassen kombiniert, weil Ich nie die KlasseY
direkt nutze sondern nur Klassen, die von Erben vonY
erben. Ich habe also viele verschiedene andere Klassen, die vonX
und Erben vonY
erben. Ich möchte den Compiler sagen, dass jedes Objekt, das übergeben wird immer vonX
undY
erbt.
Beispiel:class X { void methodeX(); };
class Y { void methodeY(); };
class Z : public Y {};
class A : public Z public X {};
class B : public Z public X {};
std::vector<Objekt das A oder B sein kann*> vector; for (Objekt das A oder B sein kann* : vector) { (*Objekt).methodeX(); (*Objekt).methodeY(); }
Das untere ließe sich mit
auto
lösen, aber ich kann ja kein array des typs auto deklarieren. (auto
würde außerdem nicht ganz den Kern treffen, denn ich weiß ja, dass das Objekt immer vonX
undY
erbt)Ich weiß nicht, ob sich das ansatzweise sinnvoll oder verständlich anhört, aber Danke für alle Antworten
-
Generell würde ich sagen, dass du das Thema Polymorphie noch nicht ganz verstanden hast.
In deinem konkreten Fall würde ich die Klasse Z von X und Y erben lassen, und alle weiteren A,B,... dann jeweils von Z.
-
Kannst du denn keine Basisklasse dafür anlegen und davon erben lassen?
class Base : public Z, public X { }; class A : public Base { }; class B : public Base { };
So kannst du dann einen
std::vector<Base*>
dafür benutzen (oder mittels Smartpointer).
-
std::variant und std::visit?
Ich weiß nicht, ob sich das ansatzweise sinnvoll ...
Hört sich merkwürdig an.
-
- Z ist eine Klasse aus einer Externen Bibliothek
- Exakt das habe ich gemacht, nur mit der Klasse
Z
dazwischen.A
undB
erben ja schon vonX
undY
, nur zwischenY
undA
liegt halt nochZ
. Aber dass alle vonX
undY
erben ist ja nicht das Problem, sondern wie ich dem Array das sage. Z.BPlayer
stammt von einem Erbe vonDrawable
, nämlichCircle
und vonRenderObject
ab.Enemy
dagegen stammt vonRectangle
, welches auch vonDrawable
erbt, undRenderObject
ab. Ich kann also keine Klasse kreieren, dieRenderObject
undDrawable
vereint, weil ich gar nicht selber vonDrawable
erbe, sondern nur von Erben vonDrawable
, die alle verschieden sind.
Vieleicht ist das Übersichtlicher: https://ibb.co/SxR5zHh
-
@Th69 Das geht nicht, denn es gibt nicht nur
Z
als Erbende Klasse von X. Um genauer zu sein, istY
die KlasseDrawable
und es gibt 'zig völlig verschiedene externe Klassen, die davon erben, wie z.BRectangle
oderText
oderCircle
. An denen kann ich nichts verändern. Ich habe meine eigene KlasseRenderObject
, von der alle meine eigenen Klassen, wiePlayer
oderEnemy
erben sollen. Aber halt auch von einer der Klassen, die vonDrawable abstammen
. Und jetzt brauche ich einen Container für Objekte, die sowohl vom externenDrawable
als auch von internen Klassen erben und sonst keinen gemeinsamen Nenner haben
-
Passt, ich muss
X
vonY
erben lassen und das array als TypY
deklarieren. So wird halt doppelt geerbt, was mit seinen Nachteilen kommt, aber wenn es nicht anders geht...
-
Das hört (bzw. liest) sich nach einem Design-Fehler an.
Die doppelte Vererbung von
Y
bedeutet aber auch, daß es zweiY
-Basisklassenobjekte gibt und du dann mittelsX
auf andere Daten zugreifst als der Zugriff überZ
. Du müßtest, wenn schon, dannvirtual
vererben (aber das geht nur, wenn du auch den Source von den verschiedenenZ
d.h. vonDrawable
abgeleiteten Klassen, ändern könntest).
Alternativ könntest du nochstd::vector<X*>
benutzen und perdynamic_cast<Y*>
aufY
zugreifen (oder umgekehrt).Gerade aber bei Spielen (oder spiele-artigen Projekten) sollte man Komposition anstatt Vererbung (für z.B.
Player
oderEnemy
etc.) benutzen, s.a. Entity component system.Edit: Hier noch zwei weitere (englischsprachige) Links (u.a. mit Diagrammen dazu):
-
Das klingt für mich komisch. Vlt ist ein Vector in dem Player und Enemy zusammen vorkommen können, einfach die falsche Datenstruktur.
Wenn du drüber iterieren willst, um gleiche Funktionen aus einer gemeinsamen Basisklasse aufrufen zu können, könntest du auch direkt einen Pointer von der nehmen.
Benötigst du die Vererbung tatsächlich zur Runtime? Sonst ist vielleicht "Compile Time Polymorphism" via Templates die richtige Lösung für dich.
Hier:
https://web.alcf.anl.gov/~zippy/publications/presentations/CompileTimePolymorphism/IBMWatsonTalk.pdf (ich habe nicht alles gelesen, aber die Funktionsweise von Compiletime Polymorphy passt)oder hier:
https://www.c-plusplus.net/forum/topic/325513/compiletime-polymorphism
-
@daniel sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
Ok, ich habe versucht da eine Lösung zu finden, und wundere mich über die schöne neue C++ Welt immer mehr. Man erwartet, doch, dass es mit
shared_ptr
möglich sein sollte den altmodischen Code darunter zu ersetzen. Irgend wie funktioniert das nicht. Weiß jemand weshalb der dynamic_pointer_cast nicht funktioniert wie er soll?#include <vector> #include <memory> #include <iostream> class X { public: void virtual methodeX() { std::cout << "methodeX\n"; } }; class Y { public: void virtual methodeY() { std::cout << "methodeY\n"; } }; class Z : virtual public Y {}; class A : virtual public Z, virtual public X {}; class B : virtual public Z, virtual public X {}; using namespace std; int main () { std::vector<shared_ptr<Y>> vy; std::vector<Y*> v; Z z; A a; B b; v.push_back(&z); v.push_back(&a); v.push_back(&b); vy.push_back(make_shared<Y>(Z{})); vy.push_back(make_shared<Y>(A{})); vy.push_back(make_shared<Y>(B{})); for (auto sp: vy) { sp->methodeY(); shared_ptr<X> xp = dynamic_pointer_cast<X>(sp); X* p = xp.get(); cout << "p=" << p << "\n"; if (p) { xp->methodeX(); } } cout << "\n"; for (size_t i = 0, e = v.size(); i != e; i++) { v[i]->methodeY(); X* p = dynamic_cast<X*>(v[i]); if (p) { p->methodeX(); } } }
-
@john-0 aus interesse:
Was geht denn nicht, was gehen sollte?Schon gesehn...
Ich würde die Nullpointer Überprüfung direkt auf dem Shared_ptr machen:if (xp != nullptr) { xp->methodeX(); }
-
@Th69 Ja mit ECS habe ich mich beschäftigt, aber das ist mir derzeit noch zu anspruchsvoll
-
@john-0 Du fügst Ys ein.
vy.push_back(make_shared<Z>()); vy.push_back(make_shared<A>()); vy.push_back(make_shared<B>());
-
@manni66 sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
@john-0 Du fügst Ys ein.
Ja, und intuitiv ist es nicht, nicht den Basistyp zu verwenden. Ok, danke für den Hinweis auf das richtige Vorgehen.
-
@john-0 sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
Ja, und intuitiv ist es nicht, nicht den Basistyp zu verwenden
Eigentlich schon. Du ersetzt new durch make_shared. Man schreibt nicht
new Y{Z{}}
sondernnew Z
wenn man es einem Y* zuweisen will, weil man es richtig gelernt hat, nicht weil es intuitiv ist..
-
@manni66 sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
Eigentlich schon. Du ersetzt new durch make_shared. Man schreibt nicht
new Y{Z{}}
sondernnew Z
wenn man es einem Y* zuweisen will, weil man es richtig gelernt hat, nicht weil es intuitiv ist.Hm, die Rolle von
new Z
übernimmt dochZ{}
(man übergibt ein Objekt und keinen Zeiger mehr auf ein Objekt) und man will je einen SmartPointer von TypY
haben. Mit Boost funktionierte das ganz noch so:boost::shared_ptr<Y> p (new Z);
Deshalb meine Verwirrung. Ok, wieder etwas dazu gelernt, aber vieles vom neuen C++ ist nicht wirklich gut.
-
@john-0 sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
Mit Boost funktionierte das ganz noch so:
boost::shared_ptr<Y> p (new Z);Das geht mit std:: genauso, make_shared ist nicht zwingend. Es hat aber z.B. den Vorteil, dass die abgeleiteten Objekte hier korrekt gelöscht werden, obwohl der virtuelle Destruktor in den Basisklassen fehlt.
-
@john-0 sagte in Ein Array kreieren, das verschiedene Klassen, die alle von X und Y erben, enthält:
Deshalb meine Verwirrung. Ok, wieder etwas dazu gelernt, aber vieles vom neuen C++ ist nicht wirklich gut.
Was konkret ist denn nicht gut?
Du musst std::make_shared ja auch mit boost::make_shared vergleichen und std::shared_ptr mit boost::shared_ptr.
Hier ist ein Artikel, warum man das besser mit
make_XXX
stattXXX_ptr<...>(new ...)
machen sollte: https://herbsutter.com/gotw/_102/