Parametrisieren von Komponenten



  • Man kann das Ergebnis des dynamic_cast auch zwischenspeichern. Dann wird es noch einfacher.

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        for(int i=0; i < ComponentCount; i++)
        {
            TLabel* label = dynamic_cast<TLabel*>(Components[i]);
            // Alle Labels verändern
            if (label)
            {
                label->Caption = "Alle Labels auf gleichen Wert setzen";
            // oder eine spezielle Behandlung
                if (label->Name == "Label1")
                    label->Caption = "Ausnahme Label1";
            }
        }
    }
    


  • audacia schrieb:

    Und innerhalb von Formularmethoden solltest du das globale Form1 -Objekt nicht verwenden.

    Danke für den Tip. Genau das war das Problem. Nachdem ich Form1 rausgenommen habe, gab es keine Zugriffsverletzung mehr.

    Verstanden habe ich es allerdings noch nicht. Warum funktioniert der gleiche Code

    for(int i=0; i < Form1->ComponentCount; i++)
    // ..
    

    (mit Form1) im OnShow ohne Zugriffsverletzung, aber im C'tor ausgeführt gibt es eine EAccessViolation Zugriffsverletzung 😕 .



  • Hallo

    Weil im Konstruktor von TForm1 der globale Zeiger Form1 noch gar nicht auf die richtige Speicheradresse gesetzt ist, das passiert erst nach dem Konstruktor.
    Ich rate sogar generell von der Verwendung der globalen Zeiger ab, die sind nur für die Bequemlichkeit von Anfängern.

    bis bald
    akari



  • OK, danke hab ich jetzt verstanden. Ist das so richtig umgesetzt, die FAQ Version und die von Braunstein ?

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
     for ( int i=0; i < ComponentCount; ++i )
    	{
    		// Alle Labels verändern
    		if ( dynamic_cast<TLabel*>(Components[i]) != 0 )
    		  {
    		   if ( dynamic_cast<TLabel*>(Components[i])->Caption != 0 )
    			  dynamic_cast<TLabel*>(Components[i])->Caption = "Alle Labels auf gleichen Wert setzen";
    		  }
    
    		// oder eine spezielle Behandlung
    		if ( dynamic_cast<TLabel*>(Components[i]) != 0 )
    		  {
    			if ( dynamic_cast<TLabel*>(Components[i])->Name == "Label1" )
    			   dynamic_cast<TLabel*>(Components[i])->Caption = "Ausnahme Label1";
    		  }
    	}
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
     for ( int i=0; i < ComponentCount; ++i )
    	{
    	 if ( dynamic_cast<TLabel*>(Components[i]) != 0 )
    	   {
    		TLabel* label = dynamic_cast<TLabel*>(Components[i]);
    		// Alle Labels verändern
    		if ( label )
    		  {
    		   label->Caption = "Alle Labels auf einen anderen Wert setzen";
    		   // oder eine spezielle Behandlung
    		   if ( label->Name == "Label2" )
    			  label->Caption = "Ausnahme Label2";
    		  }
    	   }
    	}
    }
    //---------------------------------------------------------------------------
    

    Die Version von Braunstein finde ich da übersichtlicher.



  • Ja, fast 😉

    Bei der 2. Version kannst du die Zeile

    if ( dynamic_cast<TLabel*>(Components[i]) != 0 )
    

    komplett löschen (da du das ja in der folgenden Zeile nochmals abfragst!)

    Braunstein hat diese Abfrage ja auch nicht mehrfach in seinem Code ausgeführt.

    Und ja, die 2. Variante sollte man auf jeden Fall benutzen (also nicht mehrfach den dynamic_cast<> für denselben Typen aufrufen. Bei Abfrage von mehreren Typen müssen die dynamic_cast<> natürlich entsprechend oft ausgeführt werden).



  • audacia schrieb:

    Ronco11 schrieb:

    dynamic_cast<TLabel*>(Form1->Components[i])->Caption
    

    Immer das Ergebnis von dynamic_cast<> überprüfen;

    Hm, dann hab ich den Hinweis von audacia nicht richtig verstanden 😕



  • audacia meinte sicherlich, dass du bei jedem neuen dynamic_cast abfragen solltest. Wenn du immer wieder die gleiche Variable castest machen weitere Tests keinen Sinn.
    Allerdings sollte man nur einmal casten und dann mit dem erhaltenen Wert weiterarbeiten. So ganz billig ist ein dynamic_cast nämlich nicht.
    Lies am besten mal was darüber.



  • akari schrieb:

    Weil im Konstruktor von TForm1 der globale Zeiger Form1 noch gar nicht auf die richtige Speicheradresse gesetzt ist, das passiert erst nach dem Konstruktor.

    Genaugenommen passiert es tatsächlich vor dem Konstruktoraufruf. In Application.CreateForm() wird zu einem sehr ungewöhnlichen Trick gegriffen, um das zu ermöglichen:

    procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
    var
      Instance: TComponent;
    begin
      ...
      Instance := nil;
      try
        ...
        Instance := TComponent(InstanceClass.NewInstance); // <-- Allokation
        TComponent(Reference) := Instance; // <-- Zuweisung der globalen Variable
        try
          Instance.Create(Self); // <-- Konstruktoraufruf
        except
          TComponent(Reference) := nil;
          raise;
        end;
        ...
    end;
    

    Deswegen hatte ich die Fehlerursache dort eigentlich auch nicht vermutet. Bei einem einfachen Test dieser Art

    __fastcall TForm1::TForm1(TComponent* Owner)
    	: TForm(Owner)
    {
    	Form1->Caption = "FooBar";
    }
    

    funktionierte bei mir auch alles wie erwartet.

    Wenn das Formular natürlich nicht durch Application.CreateForm(), sondern mit new() erstellt wird, ist das anders.

    Braunstein schrieb:

    So ganz billig ist ein dynamic_cast nämlich nicht.

    Im Prinzip ist das richtig, aber für Delphi-Klassen (d.h., alles, was von TObject erbt) ist es tatsächlich sehr billig; im Idealfall ist es nur ein einziger Zeigervergleich. Dies ist der Fall, weil für Delphi-Klassen keine Mehrfachvererbung unterstützt wird; daher muß der Objektzeiger nicht justiert werden, und die Liste der Vorfahren ist tatsächlich nur eine Liste und kein Graph.



  • Bezüglich Delphi hast du natürlich recht. Ist der dynamic_cast dann hier so implementiert, dass er zwischen Delphi-Klassen und C++ Klassen unterscheidet?
    Gibt es irgendwo Informationen wie genau der dynamic_cast beim BCB implemetiert ist?



  • Braunstein schrieb:

    Bezüglich Delphi hast du natürlich recht. Ist der dynamic_cast dann hier so implementiert, dass er zwischen Delphi-Klassen und C++ Klassen unterscheidet?

    Ja. dynamic_cast<> benötigt RTTI, um Klassen zur Laufzeit identifizieren zu können, und die ist für Delphi- und C++-Klassen völlig unterschiedlich.

    Braunstein schrieb:

    Gibt es irgendwo Informationen wie genau der dynamic_cast beim BCB implemetiert ist?

    Du kannst es selbst nachlesen in $(BDS)\source\cpprtl\Source\except\xxtype.c .



  • Danke.


Anmelden zum Antworten