RAD Studio 2007: Stack Overflow in TForm Konstruktor
-
Hallo,
ich habe hier ein eigenartiges Problem. Wenn ich folgenden Konstruktor für ein Formular habe
#include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> class TForm1 : public TForm { public: __fastcall explicit TForm1( TComponent* Owner, int Index ) : TForm( Owner ) { } };
und ich ein neues Formular erzeuge
TForm1* f = new TForm1( this, 0 );
bekomme ich einen Stack Overflow (0xc00000fd) durch den Konstruktor von
TForm
. Wir haben herausgefunden, dass es am zweiten Parameter liegt, wenn es einint
ist läuft der Stack über, für alle anderen Datentypen funktioniert es richtig. Hat jemand eine Erklärung dafür? Ist das bei anderen Builder Versionen auch noch so?
-
Nach einigem Suchen bin ich auf die Erklärung des Phänomens gestoßen:
In Delphi dürfen Konstruktoren
virtual
sein, und in der KlasseTCustomForm
gibt es einen virtuellen Konstruktor der FormTCustomForm( TComponent* Owner, int Dummy )
. Wenn das eigene Formular einen Konstruktor der FormTMyForm( TComponent* Owner, int Param )
hat überschreibt der Konstruktor den virtuellen Konstruktor, was dann zum Stack Overflow führt.TForm
ruft in seinem Konstruktor den virtuellen KonstruktorTCustomForm( TComponent*, int )
auf, durch das Überschreiben wird allerdings wieder der Konstruktor vonTMyForm
aufgerufen und das Elend nimmt seinen Lauf.Workaround:
Keine Konstruktoren mit der o.g. Signatur definieren.Dieses Verhalten wird Ihnen präsentiert von Borland, Inc. (c) 1999.
Sage und schreibe 15 Jahre!
-
Bei Rad Studio XE6,RS XE5, C++Builder XE4,CB XE3 und RS XE2 tritt der Stackoverflow nicht auf.
Dieses Verhalten wird Ihnen präsentiert von Embarcadero.
-
Was hat es mit Delphi zu tun? Ich hab jetzt seit so 10 Jahren kein Delphi mehr programmiert, hätt keine Ahnung mehr gehabt, dass Konstruktoren virtuell sein können. Aber das schon doch irgendwie doch eher nach C++ aus
-
DocShoe schrieb:
Dieses Verhalten wird Ihnen präsentiert von Borland, Inc. (c) 1999.
Sage und schreibe 15 Jahre!Dieses Verhalten wird Ihnen präsentiert von der Sprache C++, in der man versehentlich Funktionen der Basisklasse überschreiben kann, weil es kein
override
-Keyword gibt.
Sage und schreibe 31 Jahre (seit cfront).In Delphi wäre das nicht passiert, dort ist
override
Pflicht, und du hättestreintroduce
verwenden müssen, um deinen Konstruktor wunschgemäß zu definieren. Außerdem gibt es dort named constructors, so daß du dem Problem hättest ausweichen können, indem du deinem Konstruktor einen sprechenden Namen gegeben hättest. Und du wirst mir sicher zustimmen, daß dein Problem in keiner Weise spezifisch für virtuelle Konstruktoren, oder allgemein den C++Builder ist, sondern daß es sich bei genauerer Betrachtung um einen Designfehler der Sprache C++ handelt
-
Nein, dem Builder oder Delphi gebe ich nicht die Schuld dafür. Was mich aufregt ist die Tatsache, dass das mit keinem Wort in der Doku erwähnt wird. Der Code sieht richtig aus, funktioniert mit anderen Datentypen einwandfrei und nur mit
int
läuft der Stack über. Dann sucht man 2.5 Stunden lang den Grund dafür und findet hinterher raus, dass es irgendeinen virtuellen Konstruktor gibt, der vom eigenen überdeckt wird. Das nervt einfach nur. Zumal es jetzt auch nicht so unwahrscheinlich ist, dass Leute ihre eigenen Konstruktoren definieren, da kann es auch schon mal passieren, dass genau dieser Konstruktor überdeckt wird.PS:
Ich bin doch etwas stinkig auf Delphi. So weit ich das verstanden habe wird dieser virtuelle Konstruktor nur intern für das Resource Streaming benutzt. Welche Rolle dieserint
Parameter dabei spielt weiß ich nicht, aber bei der Benennung Dummy habe ich das Gefühl, dass er nur zur Definition der Signatur benutzt wird. Und da hätte man doch besser einen eigenen Datentyp (z.B. sowas wie TFormCreateContext) benutzen können, um der versehentlichen Überdeckung vorzubeugen.@Burkhi:
Kannst du mal die Signatur vonTCustomForm::CreateNew
ab XE2 posten? Würde mich mal interessieren, wie die jetzt aussieht.
-
DocShoe schrieb:
Was mich aufregt ist die Tatsache, dass das mit keinem Wort in der Doku erwähnt wird. [...] Zumal es jetzt auch nicht so unwahrscheinlich ist, dass Leute ihre eigenen Konstruktoren definieren, da kann es auch schon mal passieren, dass genau dieser Konstruktor überdeckt wird.
Das ist berechtigte Kritik, der ich mich anschließe.
DocShoe schrieb:
Ich bin doch etwas stinkig auf Delphi. So weit ich das verstanden habe wird dieser virtuelle Konstruktor nur intern für das Resource Streaming benutzt. Welche Rolle dieser
int
Parameter dabei spielt weiß ich nicht, aber bei der Benennung Dummy habe ich das Gefühl, dass er nur zur Definition der Signatur benutzt wird.Richtig. Das Problem ist, daß Delphi zwar named constructors unterstützt, aber C++ keine Syntax dafür vorsieht. Deshalb müssen, wenn C++-Kompatibilität erwünscht ist, Konstruktoren in Delphi sich in auch der Signatur unterscheiden, da der OBJ-Generator des Delphi-Compilers bei Konstruktoren name erasure durchführt und es sonst Duplikate gibt. Der Delphi-Compiler gibt bei Bedarf eine entsprechende Warnung aus.
DocShoe schrieb:
Kannst du mal die Signatur von
TCustomForm::CreateNew
ab XE2 posten? Würde mich mal interessieren, wie die jetzt aussieht.Ich postuliere mal folgendes: sie ist unverändert, aber Burkhi hat seine Ableitung von TForm nicht mit dem Formulardesigner erstellt, sondern direkt im Code. Dadurch gibt es keine DFM, und deshalb kommt der Streaming-Mechanismus nicht ins Spiel => kein Stack-Overflow.
-
audacia schrieb:
DocShoe schrieb:
Was mich aufregt ist die Tatsache, dass das mit keinem Wort in der Doku erwähnt wird. [...] Zumal es jetzt auch nicht so unwahrscheinlich ist, dass Leute ihre eigenen Konstruktoren definieren, da kann es auch schon mal passieren, dass genau dieser Konstruktor überdeckt wird.
Das ist berechtigte Kritik, der ich mich anschließe.
Ich revidiere: Das Verhalten ist dokumentiert. Siehe hier:
http://docwiki.embarcadero.com/Libraries/XE6/en/Vcl.Forms.TForm.CreateEmbarcadero DocWiki schrieb:
/* TCustomForm.Create */ inline __fastcall virtual TForm(System::Classes::TComponent* AOwner) : TCustomForm(AOwner) { } /* TCustomForm.CreateNew */ inline __fastcall virtual TForm(System::Classes::TComponent* AOwner, int Dummy) : TCustomForm(AOwner, Dummy) { } /* TWinControl.CreateParented */ inline __fastcall TForm(HWND ParentWindow) : TCustomForm(ParentWindow) { }
[...]
Pass a Component and an integer to create the form while bypassing the streaming system that loads a form’s properties and descendants from a .DFM file. The second parameter (Dummy) serves only to distinguish this constructor from the first. This constructor is rarely used for form objects. When using this syntax, stream in an external .DFM to bind the visual components with their classes. WriteComponentResFile and ReadComponentResFile must bracket the constructor call. The following code sequence:
Streams out an external .DFM file.
Creates a new form disassociated from any .DFM resource file.
Streams in the external .DFM file and binds it to this new form.WriteComponentResFile("Temp.dfm", Form1);
// ...
Form2 = new TForm(Application, 1);
ReadComponentResFile("Temp.dfm", Form2);So steht das übrigens auch schon in der Doku zu C++Builder 6.
-
In der installierten Hilfe zum RAD Studio 2007 gibt es 6 Einträge für
Create
. Bei keinem davon wird auf das Verhalten hingewiesen. UnterCreateNew
wird lapidar CreateNew wird von Create aufgerufen aufgeführt. Da muss man aber schon etwas genauer überlegen, was das eigentlich bedeutet und welche Auswirkungen das hat. Wenn man überhaupt den Weg bis zuCreateNew
gefunden hat. Es ist schon ein bisschen so wie die Nadel im Heuhaufen.Aber Schwamm drüber, ich hab mich ja schon fast wieder beruhigt
-
DocShoe schrieb:
In der installierten Hilfe zum RAD Studio 2007
Die Hilfe hatte einen Tiefpunkt nach C++Builder 6, wobei reihenweise Inhalte verloren gingen, die erst nach und nach wiederhergestellt wurden. Trotzdem zählt das Argument eigentlich nur, wenn du bei der Arbeit keinen Internetanschluß hast, um auf das recht gut gefüllte Docwiki zuzugreifen
-
audacia schrieb:
...Ich postuliere mal folgendes: sie ist unverändert, aber Burkhi hat seine Ableitung von TForm nicht mit dem Formulardesigner erstellt, sondern direkt im Code. Dadurch gibt es keine DFM, und deshalb kommt der Streaming-Mechanismus nicht ins Spiel => kein Stack-Overflow.
Ja, das hatte ich gemacht, wenn dann dadurch der Streaming Mechanismus nicht zum Tragen kommt, der diesen Stackoverflow auslöst, ist klar, das es dann keine Probleme gibt.
Aber das ging m.E. auch nicht aus dem Ausgangsthread hervor. Aber wir haben uns ja nun alle wieder lieb.