Programmablauf C++
-
viande schrieb:
Mist, voll verlesen ... hattest es ja schon selber rausgefunde ... :p
Ich vereinfache einmal das Problem, vor dem ich stand:
class FwzBaseClass{ const USHORT fieldsize; // soll immer 10 sein ULONG lastrecord; // ... und noch einige weitere Elemente public: FwzBaseClass(HFILE* tablefile); USHORT fdsize(); };
Dem Konstruktor der Klasse sollte ein Dateihandle übergeben werden, anhand
der Signatur der Datei sollte der Konstruktor herauslesen, an welcher Byte-Position der letzte Datensatz beginnt, und den Wert (etwa nach einem Aufruf von atol()) im ULONG Member 'lastrecord' abspeichern. Egal, welche Datei gerade geöffnet wurde, jedes Feld innerhalb eines FileWizard Datensatzes ist immer 10 Byte gross (es haengt nur von der Typmaske ab, ob diese 10 Bytes als Daten oder als Referenz interpretiert werden), das heisst, fieldsize ist IMMER 10 Byte gross, und es ist naheliegend, eine Konstante dafuer in der Klasse fwzBaseClass abzulegen. Mit dem KonstruktorFwzBaseClass(HFILE* tablefile);
ist es aber natuerlich nicht moeglich, die Konstante fieldsize mit dem Wert 10 zu versehen, da Konstanten nur innerhalb einer Initialisierungsliste eines Konstruktors Werte zugewiesen werden können, und nirgendwo sonst. Es muesste also heißen:
FwzBaseClass(HFILE* tablefile):fieldsize(10){}
aber bekanntlich müssen in diesem Fall die Funktionsrumpfklammern gleich daneben stehen (es ist also nicht möglich, den Konstruktorrumpf irgend wo anders zu definieren:
FwzBaseClass::FwzBaseClass(HFILE* tablefile){ // FEHLER! KONSTRUKTOR-FUNKTIONSRÜMPFE MÜSSEN IN DER KLASSENDEFINITION STEHEN! }
klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).
Das Problem lässt sich auch kurz formulieren:
- Eine Klasse enthält non-static konstante Elemente (fieldsize)
- Außerdem gibt es auch variable non-static Elemente (lastrecord)
- beide Elemente soll der selbe Konstruktor initialisieren, wobei die Initiali-
sierung des variablen Elements so komplex ist, dass sie nur im Konstruktor-
rumpf stattfinden kannLösung:
OHNE Ableitungsmechanismus gibt es innerhalb der Standard-C++ Syntaxregeln
GAR keine Lösung für das Problem. Die einzige Lösung besteht hierin:class FwzBaseClass{ protected: const USHORT fieldsize; public: FwzBaseClass():fieldsize(10){} // Jetzt ist der Konstruktor nicht rechtswidrig }; class TableDescriptor: public FwzBaseClass{ protected: ULONG lastrecord; // Unmengen an weiteren Daten (ULONG* RecordTable z.B.) public: TableDescriptor(HFILE* tablefile); // und diesen Konstruktor kann ich // jetzt problemlos irgendwo anders // definieren, und kann dann für die // Berechnungen sogar schon auf den // Wert von fieldsize == 10 zurückgreifen // weil der Basisklassenkonstruktor zuerst // kommt // ... Unmengen an weiteren Funktionen und Funktiönchen (das Zergliedern // des Monsterprojekts hat mir hier wirklich geholfen, THX @Shade, // THX @simonphoenix THX @otze THX @mastah };
Jedenfalls scheint es die einzige Möglichkeit in einer Klassenhierarchie zu sein, Konstante Members mit Werten zu versehen, dass man die Konstanten wie in einem Container in der Basisklasse zusammenfasst, wo sie dann über die Initialisierungsliste des Basisklassenkonstruktors mit Werten versehen werden können.
// Meiner Meinung nach sollte es einem Konstruktor auch erlaubt sein, die
// Werte von Member-Konstanten innerhalb seines Funktionsrumpfes zu mani-
// pulieren, denn der oben skizzierte 'Ableitungszwang' ist unbefriedigend
// und umständlich
-
Mecnels mal was anderes, warum benutzt du immer HFILE? Wenn du mit CreateFile arbeitest dann nimmst man HANDLE.
-
anti-HFILE schrieb:
Mecnels mal was anderes, warum benutzt du immer HFILE? Wenn du mit CreateFile arbeitest dann nimmst man HANDLE.
THX für den TIPP. Da hätte ich mir oft das explizite casten zu (HANDLE) ersparen können, wär' mir das gleich eingefallen. Jedenfalls ist die Library FileWizard fast fertig (hab seit Sonntag 38 (!!) Stunden wie ein Freak Code reingehackt). Das System operiert ähnlich wie ein Betriebssystem: Jedes Feld ist 10 Byte groß, jede Datei kriegt eine 'Maske' in der Signatur verpasst, die festlegt, ob die 10 Byte als Daten oder als Referenz zu interpretieren sind. Bei kleinen Datenmengen (10.000 Datensätze mit der Maske iiisssis - also int_int_int_string_string_string_string_int_string - wobei string immer eine Referenz ist:)) funzt das Teil super.
Trotzdem Danke für den Extra-Tipp mit dem HANDLE.
-
Mecnels schrieb:
klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).
uh, das wär aber unpraktisch.
struct Bar { int &i_; Bar (int &i) : i_(i) { i = 23; } };
warum sollte das verboten sein?
-
davie schrieb:
Mecnels schrieb:
klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).
uh, das wär aber unpraktisch.
struct Bar { int &i_; Bar (int &i) : i_(i) { i = 23; } };
warum sollte das verboten sein?
Sry mein Fehler, hab den Konstruktorrumpf außerhalb der Klassendefinition geschrieben. Sry. Innerhalb der Klassendefinition ist das ok.
Nur - ab einer gewissen Länge des Funktionsrumpfs einer Memberfunktion bild ich mir ein, die Funktionsdefinition außerhalb der Klassendefinition unterbringen zu müssen (keine Ahnung warum, schlechte Angewohnheit wahrscheinlich.)
Trotzdem hast Du recht, rein syntaktisch klappt es, wenn der Funktionsrumpf des Konstruktors innerhalb der Klassendefinition steht.
-
was für ein problem du mit den initialisierungslisten + leere funktionsrümpfe du hast ist, ist mir schleierhaft.
initialisierungslisten sind teil der Definition des Konstructors, daher der zwang, gleich anschliessend die { } Klammern zu bringen, aber das muss keineswegs bei der klassendefinition passieren. es funktioniert z.b. das hier
test.h#ifndef TEST_H #define TEST_H struct foo { const int a; int b; int c; foo(int x); }; #endif
test.cpp
#include "test.h" foo::foo(int x) : b( a ), a( x ) { c = a; }
main.cpp
#include <iostream> #include "test.h" int main() { foo bar( 1 ); std::cout << bar.a << '\t' << bar.b << '\t' << bar.c << std::endl; return EXIT_SUCCESS; }
das funktioniert sogar mit einem compiler eines softwareherstellers aus redmond.