Objekt über Formulare hinweg beibehalten



  • Eine triviale Frage:

    Ich habe eine Klasse mit Header- und Cpp-Datei erstellt.

    Im ersten Formular binde ich die Klasse ein, erstelle ein Objekt und fülle es (ne map und multimap aus der STL befinden sich dahinter).
    Kann den Inhalt der maps der Klasse in Form1 auch auslesen, erweitern usw.
    Soweit so gut.

    Im zweiten Formular möchte ich auf das soeben in Form1 gefüllte Objekt zugreifen, es erweitern, kürzen etc. Wenn Form2 geschlossen wird, soll der geänderte Inhalt nicht verloren gehen, sondern in Form1 zur Verfügung stehen.

    Beide Formulare sollen sich also das Objekt teilen. M. a. W.: Wie übergebe ich das Objekt nach Form2 so, dass ich es in Form2 bearbeiten kann und der Inhalt auch nach Schließen von Form2 weiter erhalten bleibt? Danke.



  • Wenn Du das Objekt mit new erzeugst, kannst Du dessen Zeiger direkt an Form2 übergeben, ansonsten mußt Du eine Referenz auf das Objekt an Form2 übergeben.



  • Problem gelöst. Das Objekt muss in der Headerdatei von Form1 erstellt werden.
    Danke.



  • Ingo schrieb:

    Das Objekt muss in der Headerdatei von Form1 erstellt werden.

    Mach ich nie so. Das kommt mir nicht so vor, als ob das ein guter Programmierstil ist.
    Könntest Du mal Deine Problemlösung zeigen?

    Gruß,

    Alexander



  • Gern. Das hier ist der entscheidende Ausschnitt aus der Header-Datei von Unit1.cpp (also Unit1.h):

    class TForm1 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
    	TButton *Button1;
    	TButton *Button2;
    	TSpeedButton *SpeedButton1;
    	TSpeedButton *SpeedButton2;
    	TOpenDialog *OpenDialog1;
    	TSaveDialog *SaveDialog1;
    	void __fastcall Button1Click(TObject *Sender);
    	void __fastcall SpeedButton2Click(TObject *Sender);
    	void __fastcall SpeedButton1Click(TObject *Sender);
    private:	// Anwender-Deklarationen
    public:		// Anwender-Deklarationen
    	__fastcall TForm1(TComponent* Owner);
    	buchfuehrung bf;
    };
    //---------------------------------------------------------------------------
    

    Die letzte Zeile ist entscheidend: buchfuehrung bf.
    Mit dem Objekt bf kann ich jetzt über alle Units/Formulare hinweg arbeiten, fungiert quasi als globale Variable.
    Ist daran was nicht gut?



  • Mir fallen dazu mehrere Dinge ein:
    1. Zum Thema globale Variablen möchte ich nur sagen, dass ich derzeit gerade mit mehreren Projekten kämpfe, die nicht von mir begonnen wurden, aber
    jetzt von mir weiterentwickelt werden dürfen, in denen massiv globale und öffentliche Variablen eingesetzt werden. Mit der Auswirkung, dass Du an
    einer Stelle schraubst und dann hoffst, dass sich die Änderungen auch wirklich nur an dieser Stelle auswirken (was sie so gut wie nie tun). Besser
    man gewöhnt sich's erst gar nicht an.

    2. Den Variablennamen bf würde ich vermeiden. Ich verwende grundsätzlich sprechende Variablennamen. Außnahmen sind z.B. for-Schleifen-Zähler. Praktisch
    in dem Zusammenhang finde ich eigentlich die Borland-Konvention Klassennamen mit einem "T" beginnen zu lassen, die Member-Variable mit einem "F" und
    die Eigenschaft dann einfach ohne Prefix. Also z.B.: class TBuchfuehrung, TBuchfuehrung FBuchfuehrung, __property TBuchfuehrung* Buchfuehrung - das
    ist aber wohl auch eher Geschmacksache; Hardcore-C++-Programmierer würden hier garantiert eine andere Konvention vorschlagen.

    3. Wenn buchfuehrung eine Klasse ist, finde ich es ein wenig ungewöhnlich die Member-Variable nicht als Referenz anzulegen, also

    buchfuehrung* bf;
    

    Vielleich bin ich da aber auch der einzige.

    Nach meinem heutigen Kenntnisstand würde ich das Problem vielleicht so angehen:

    // Header-Datei
    class TForm1 : public TForm
    {
    __published:
      //...
    private:
      buchfuehrung* bf;
    
      buchfuehrung* getBuchfuehrung();
    public:
      __fastcall TForm1(TComponent* Owner);
      virtual __fastcall ~TForm1();
    
      __property buchfuehrung* Buchfuehrung = {read=getBuchfuehrung};
    };
    
    __fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)
    {
      bf = 0;
    }
    
    __fastcall TForm1::~TForm1()
    {
      delete bf;
    }
    
    buchfuehrung* TForm1::getBuchfuehrung()
    {
      if (!bf)  // Lazy instantiation
        bf = new buchfuehrung();
      return bf;
    }
    

    Wenn Du "lazy instantiation" verwendest, solltest Du (bis auf Kon- und Destruktor) auch innerhalb der Klasse TForm1 immer auf die Eigenschaft
    zugreifen und nie auf die private Variable. Stattdessen kann man natürlich auch an einer Stelle, an der klar ist, dass noch kein Zugriff auf bf
    erfolgt, die Instanz erzeugen - vielleicht im Konstruktor.

    Es gibt sicherlich auch andere Wege, womöglich auch bessere, aber mit dem Ansatz vermeidest Du wenigstens globale Variablen.

    Ich hoffe, ich habe jetzt nicht mehr Verwirrung als Klarheit gestiftet. Wenn das für Dich alles nur kalter Kaffee ist, dann ignoriere es einfach.

    Gruß,

    Alexander



  • Hallo Alexander,

    ich kann Deinem Posting nur zustimmen. Nur in Deinem Destruktor hat sich ein kleiner Fehler eingeschlichen. Falls bf niemals verwendet wird kracht es...

    __fastcall TForm1::~TForm1()
    {
      if (bf)
        delete bf;
    }
    

    Weiterhin hast Du bf = 0; geschrieben. Das ist vollkommen richtig so. Allerdings hab ich mir angewöhnt bei Zeigerinitialisierungen bf = NULL; zu schreiben. So sieht man direkt, ob es eine Variable, oder ein Zeiger ist.



  • Danke für die Antworten, ich stell das dann mal um <seufz>.



  • Joe_M. schrieb:

    Falls bf niemals verwendet wird kracht es...

    ...nicht.

    delete 0;
    

    sollte keinen Fehler geben - habe ich mal in einem schlauen Buch gelesen. Aus meiner Erfahrung kann ich sagen, dass es bisher auch
    noch nie gekracht hat.

    Joe_M. schrieb:

    Allerdings hab ich mir angewöhnt bei Zeigerinitialisierungen bf = NULL; zu schreiben. So sieht man direkt, ob es eine Variable, oder ein Zeiger ist.

    Genau das habe ich mir abgewöhnt. Es gibt ja hier im Forum, in der C++-Abteilung, eine Abhandlung über NULL und 0, die Du wahrschein-
    lich auch schon mal gelesen hast. Letztlich ist es wahrscheinlich Geschmacksache. Ich habe allerdings bei Kollegen gesehen, dass auch int-, bool- und
    double-Variablen mit NULL initialisiert wurden, was natürlich geht, aber keine andere Bedeutung hat als mit 0 zu initialisieren. Was ich damit sagen
    will, ist, dass vielen gar nicht bewusst ist, dass NULL nicht gleichbedeutend mit "nicht initialisiert" ist. Ich habe oft den Vergleich von bool- oder
    int-Werten auf NULL gefunden, mit dem Ziel, zu überprüfen, ob die Variable schon korrekt gesetzt wurde, was natürlich für Probleme gesorgt hat, wenn der
    Wert tatsächlich einfach 0 war. Also habe ich mir angewöhnt, NULL nicht mehr zu verwenden. Aber - wie schon gesagt - Geschmacksache...oder Frage der
    firmeninternen Konventionen. Bei Java sieht das natürlich anders aus, aber das ist ein anderes Thema...

    Gruß,

    Alexander



  • hm, ist ja interessant, habe ich gar nicht gewusst. Hab's auch gleich mal ausprobiert, wobei ich auch bei der Initialisierung mit NULL keine Fehlermeldung bekomme...
    Allerdings ist auch kein Destruktor in der Testklasse vorhanden (nur deklariert). Ist mir nicht geheuer. 😉 Muss ich bei Gelegenheit mal ausgiebig testen.

    Ich werde weiterhin Zeiger mit NULL initialisieren und Variablen mit 0 und auch weiterhin auf einen gültigen Zeiger prüfen, bevor ich Objekte freigebe. Da ich mit C angefangen hab' ist das wohl noch eine Altlast... Ich hatte sowieso Schwierigkeiten von C auf C++ umzusteigen. Alte Gewohnheiten legt man nur schwer ab. 🙂


Anmelden zum Antworten