Archiv verlassen und diese Seite im Standarddesign anzeigen : C++, extrem dynamic casting
SimonX
2010-05-05, 13:49:21
Hi,
Warum geht das nicht?
#include <vector>
#include <stdio.h>
class A { };
class B: public A { };
typedef std::vector<A*> A_t;
typedef std::vector<B*> B_t;
int main()
{ A_t a;
B_t *b;
b=dynamic_cast<B_t*>(&a);
printf("%p %p \n", &a, b);
return 0;
}
Es sind beides Vektoren auf Pointer. Nur die Interpretation des Pointer wird geändert. Der Fehler ist:
x.cc:14: error: cannot dynamic_cast `&a' (of type `class A_t*') to type `class B_t*' (source type is not polymorphic)
Es geht mir um einen STL-vector, der generische A enthält, die aber über eine Template alle B sind. Dumm, das man den STL-vector nicht nach aussen geben kann damit direkt auf die B zugegriffen werden kann. Zur Zeit muss der Anwender ein eigenes dynamic_cast für jeden Zugriff machen, was sehr unhandlich ist.
Gnafoo
2010-05-05, 14:18:02
Naja vector<A*> und vector<B*> erben nicht voneinander, nur weil die Typparameter es tun. Daher funktioniert der dynamic_cast hier nicht. Ansonsten hättest du einerseits ein Problem, wenn einer der Einträge nicht vom Typ B ist (was passiert dann damit beim Cast?) und andererseits könntest du z. B. einen vector<B*> auf einen vector<A*> casten und auf einmal Objekte reinpacken, die du gar nicht reinpacken dürftest.
Lässt sich das ganze nicht ggf. vom Design her umgehen? Dynamic_cast würde ich sowieso versuchen zu vermeiden. Warum hast du nur einen vector<A*>, wenn du sowieso in dem Template weißt, dass du nur B verwendest? Kann man nicht gleich einen passenden vector verwenden? Und wieso sollte man den vector nicht nach außen geben können?
Edit: wenn du genau weißt, dass du ein Objekt vom Typ B hast, kannst du afaik auch static_cast verwenden. Das erspart dir den Type-Check vom dynamic_cast. Für vector gilt das aber nach wie vor nicht.
B ist ein A, aber A muss kein B sein. Du kannst etwas von Klasse A nicht nach B casten, sofern es nicht von Anfang an schon B war (A* a = new B z.B.). Und ob die ganze Operation dann auch noch auf Vektoren der Typen und dann auch noch hin auf einen Pointer funktioniert, weiss ich nicht, aber es klingt für mich allgemein eher unmöglich (und sinnlos), die Adresse von a in einen B-Pointer zu casten.
Nein, mittels dynamic_cast will man ja gerade einen geprüften "Upcast" mittels RTTI machen. Die Vererbung ist so schon richtig.
Das Problem dabei ist, dass A keine VTable hat, deshalb auch "source type is not polymorphic".
So müsste es gehen:
class A { virtual ~A() {} };
Gnafoo
2010-05-05, 14:28:32
Der source Type ist doch hier A_t*, also std::vector<A*>* und nicht A*.
Sorry, hab ich übersehen. Hast recht.
Man kann STL-Container nicht via dynamic_cast aufeinander abbilden. Das ergibt ja auch keinen Sinn, die Operation müsste ja den ganzen Container checken ob nur B* enthalten sind.
Was man bräuchte ist einen Wrapper um std::vector der bei jedem Zugriff das dynamic_cast macht. Evtl. hat Boost da was.
SimonX
2010-05-05, 20:27:44
Ich mache das jetzt mit reinterpret_cast, denn der vector<A*> ist Teil eines Templates für B und damit ist sichergestellt, das alle auch B sind. Da der vector selbst nur von Pointern was weis sehe ich da kein Problem. Ausserdem ist das nur read-only access auf den vector. Der vector wird nicht verändert.
Ich könnte ja auch die von mir benutzte Basisklasse, die den vector<A*> bereitstellt zu einem Template umbauen, nur dann muss man so viel Code in das Header-File schieben, was ich durch das kleine Wrapper-Template umgehen wollte.
Bist du überhaupt sicher, dass du RTTI brauchst?
Das reinterpret_cast wird wohl funktionieren, ist aber ziemlich unschön.
Gnafoo
2010-05-05, 21:02:58
Okay die Basisklasse hat den vector. Mein Vorschlag: schieb den vector in die abgeleitete Klasse, die weiß was drin ist und stelle ggf. virtual-protected-Methoden bereit, über die die Basisklasse auf den Vektor zugreift, wenn das nötig sein sollte. Vermutlich brauchst du das ja nur für eine Handvoll Zugriffe. Oder mach aus der Basisklasse einfach ein Interface und implementiere alles in der abgeleiteten Klasse (je nach Aufwand).
Wenn das öffentliche Interface für die Aufgaben der Basisklasse ausreicht, kannst du die Funktionalität ggf. auch mit normalen Funktionen anbieten.
Monger
2010-05-05, 22:00:28
Das Problem existiert in anderen Sprachen übrigens auch. Ein Container von X ist noch lange kein Container von Y, wenn Y von X erbt.
Nehmen wir mal an, wir haben eine Liste von Mitarbeitern. Die Klasse Mitarbeiter erbt von der Klasse "Person". Wenn ich also eine Liste von Mitarbeitern habe, habe ich dann nicht zugleich eine Liste von Personen? Wenn jeder Mitarbeiter eine Person ist, müsste ich dann nicht auch Personen in die Mitarbeiterliste stecken können?
Die Antwort ist natürlich nein - das würde ja bedeuten, dass man auch ganz andere Personen in diese Liste mischen könnte, die keine Mitarbeiter sind.
Um das aufzulösen, gibt es zumindest in Java und .NET auch Generic Constraints. Hab allerdings keine Ahnung, wie das in C++ aussieht.
vBulletin®, Copyright ©2000-2025, Jelsoft Enterprises Ltd.