Virtuelle Funktionen in Klasse verursachen Absturz
-
Fellhuhn schrieb:
Hat der Pointertyp nicht nur zur Compilezeit "kosmetische" Effekte? Der ist doch immer identisch, egal worauf es zeigt. Da dürfte es doch egal sein auf was für einen Pointer wie und warum gecastet wird, solange die Daten dort in Ordnung sind, oder nicht?
Nein, der Base-Zeiger zeigt nur auf ein Base, der Neu-Zeiger zeigt auf ein Neu, welches ein Base als Bestandteil hat. Gerade wenn Polymorphie ins Spiel kommt müssen das nicht identische Adressen sein.
-
Ah, du meinst bei dem Cast zum Base-Pointer wird dieser zB höher gesetzt um etwaige vtables oder was auch immer zu überspringen? Das wäre ja richtig schlecht.
-
Warum sollte das schlecht sein?
Oder aus der anderen Richtung gefragt: Wie würdest Du es sonst machen?
Spätestens bei Mehrfachvererbung muss ich ein abgeleitetes Objekt sowohl in ein Basis1-Objekt als auch in ein Basis2-Objekt umwandeln können. Die können nicht dieselbe Adresse haben (jedenfalls kann ich mir keinen effizienten Mechanismus vorstellen, der das realisieren könnte).
-
Ich war nie ein Freund der Mehrfachvererbung.

-
bei komposition würdest du doch auch nie auf die idee kommen, den zeiger auf ein objekt direkt auf einen zeiger auf ein subobjekt zu verbiegen, oder?
-
Ich hab mir das immer bisher so gedacht das das Basisobjekt an der Adresse liegt auf den der Pointer zeigt und alles zusätzliche der ableitenden Klasse dahinter. Aber das funktioniert natürlich nicht bei Mehrfachvererbung.
-
Das hast du dir so gedacht, und das koennte auch eine der moeglichen Implementierungen sein, allerdings ist das afaik im Standard nicht festgelegt, in welcher Reihenfolge Subobjekte im Speicher abgelegt werden. Des weiteren ist im Standard weder festgelegt, wie ein Pointer auszusehen hat, noch ob Pointer auf zwei verschiedene Typen aehnlich oder unterschiedlich auszusehen haben. Da der Standard keinerlei Annahmen treffen kann, wie "ungewoehnlich" die Hardware, das Betriebssystem und das Speichermanagement deiner Plattform sind, huetet er sich davor, solche Konvertierungen wie Derived* -> void* -> Base* allgemein gueltig zu machen.
Uebrigens: da du, ripmav, offensichtlich auch abgeleitete Objekte ueber einen Pointer auf die Basisklasse zerstoeren willst, hast du doch hoffentlich auch den Destruktor der Basisklasse virtuell gemacht, oder? (Das gibt sonst den Alptraum eines jeden Porgrammierers: undefiniertes Verhalten)
-
Hallo,
danke für die vielen Antworten!
Ich muss leider zu einem void-Pointer konvertieren, da die einzige Möglichkeit, einen Pointer zu übermitteln, in einer WinAPI Funktion liegt, und die nimmt nur void-Pointer an..
Ich habe mir jetzt mal die verschiedenen casts angeschaut, und so wie es aussieht, muss ich dynamic_cast verwenden (Wenn ich Neu* zu Basis* konevrtieren will). Das Problem dabei ist, dass der Compiler (MS VC++ 2005) meckert: 'LPVOID' : invalid expression type for dynamic_cast
So wie es aussieht, kann man dynamic_cast nicht mit void* verwenden..
Was sollte ich stattdessen tun? static_cast nehmen? oder gleich reinterpret_cast (dürfte in diesem Fall dieselbe Wirkung wie (Basis*)pV haben) ?Ich habe mal den Beispielcode meines ersten Posts kompiliert und der Absturz kommt gar nicht.. So wie es aussieht tritt der Fehler entweder nur unter bestimmten Vorraussetzungen auf, oder es liegt gar nicht daran, wie der Pointer konvertiert wird.. Ich werde das nochmal durchtesten..
@pumuckl:
Ich brauche in meiner Klasse keinen eigenen Desktruktor, deshalb habe ich den vom System gelassen.. Das dürfte OK sein, oder?MfG,
Max
-
Wenn du void* zur Übergabe benutzen musst, solltest du immer darauf achten, dass der (statische) Typ das Zeigers vor der Konvertierung in void* identisch mit dem ist, in den du später zurückkonvertierst. Das ist (im Wesentlichen) die einzige Garantie, die der Konvertierung in void* anhaftet - im Allgemeinen (es gibt ein paar wenige zusätzliche Garantien bei primitiven Typen, aber das kann uns hier egal sein) besteht keine bestimmte Beziehung zwischen Zeigerkonvertierungen der Form T* -> U* und T* -> void* -> U*.
Dann sollte alles gut gehen - versuch niemals, solche Probleme durch Probieren mit exotischen Casts zu lösen, selbst wenn es dann gelegentlich zu funktionieren scheint, ist es wahrscheinlich trotzdem falsch. Nebenbei: reinterpret_cast ist immer falsch - es sei denn, du weist genau, was dabei passiert.// Das steht natürlich nicht so da, sondern wird über Umwege übergeben: LPVOID pV = pNeu;hier ist der statische Typ Neu* die Konvertierung zurück muss daher ebenfalls zu Neu* erfolgen. Falls du nur garantieren kannst, dass ein Zeiger auf Basis* übergeben wird, muss zunächst in diesen Typen konvertiert werden.
Neu *pNeu = new Neu; void* some_func(void*); Basis *pTmp = pNeu; // implizit Basis* pBasis = static_cast<Basis*>( some_func( pTmp ) ); // die Konvertierung aus void* bedarf eines CastesFalls du auf die Hilfsvariable verzichten willst, kannst du notfalls auch explizit casten - da es allerdings kein äquivalentes implicit_cast gibt, wäre ein static_cast notwendig, was eigentlich zu schweres Geschütz ist. Hilfsweise kann man auch so etwas einsetzen
template<typename T, typename U> T implicit_cast(U u) { return u; } ... Basis* pBasis = static_cast<Basis*>( some_func( implicit_cast<Basis*>( pNeu ) ) );Das ist vorteilhaft, indem es einerseits explizit klarstellt, mit welchem Typen gearbeitet wird, und andererseits dies nicht auf einer potentiell gefährlichen syntaktischen Form (=Cast) beruht.
-
Hallo,
ich habe jetzt mal campers Methode ausprobiert - hat den Absturz aber leider nicht beseitigt..Inzwischen habe ich herausgefunden, dass es nicht an einem falsch konvertierten Pointer liegt, da das Problem auch auftritt, wenn ich statt der Klasse Neu die Basisklasse übergebe (also Basis* -> void* -> Basis*).
Das Problem habe ich jetzt mal im WinAPI Forum genauer erklärt, weil es nicht mehr zum Forumteil C++ gehört:
http://www.c-plusplus.net/forum/viewtopic-var-p-is-1473218.htmlDanke für die Antworten! Nun kenne ich mich (zumindestens grundlegend) mit casts aus..

MfG,
Max