Announcement

Collapse
No announcement yet.

Verständnisproblem zu Klassen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Verständnisproblem zu Klassen

    Hallo,

    ich habe anscheinend ein "leichtes" Verständnisproblem, was die Verwendung von Klassen angeht: Ich habe 2 Klassen erstellt. Dabei benutzt Klasse1 die Klasse2, indem sie 2 Objekte der Klasse2 beim eigenen Create anlegt. Die Objekte der Klasse2 sollen ihrerseits auf Eigenschaften der Klasse1 zugreifen können, weshalb ein Pointer auf das Objekt der Klasse1 beim Create übergeben wird. Das ganze funktioniert leider nur, solange ich dabei mit nur einem Objekt arbeite.
    Ich habe den Quelltext auf ein Minimum eingekürzt:

    <PRE>
    unit ClassTest;

    interface

    uses Dialogs;

    type
    { Forward-Deklarationen }
    TKlasse1 = class;

    TPointerAufKlasse1 = ^TKlasse1;

    { TKlasse2 }
    TKlasse2 = class
    public
    Owner : TPointerAufKlasse1;
    procedure ShowOwnerText;
    constructor Create(Klasse1: TPointerAufKlasse1);
    end;

    { TKlasse1 benutzt TKlasse 2}
    TKlasse1 = class
    private
    FText: String;
    FSeite1: TKlasse2;
    FSeite2: TKlasse2;
    public
    procedure ShowOwnerText;
    constructor Create(Text: String);
    destructor Destroy; override;
    published
    property Text : String read FText;
    end;

    implementation

    (************************************************* **************************
    * Implementierung für die Klasse TKlasse1 *
    ************************************************** *************************)

    constructor TKlasse1.Create(Text: String);
    begin
    FText := Text;
    FSeite1 := TKlasse2.Create(@Self);
    FSeite2 := TKlasse2.Create(@Self);
    end;

    destructor TKlasse1.Destroy;
    begin
    if FSeite1 <> nil then FSeite1.Free;
    if FSeite2 <> nil then FSeite2.Free;
    inherited;
    end;

    procedure TKlasse1.ShowOwnerText;
    begin
    FSeite1.ShowOwnerText;
    //FSeite2.ShowOwnerText; // <-- Beim Entfernen des Kommentars Laufzeitfehler
    end;

    (************************************************* **************************
    * Implementierung für die Klasse TKlasse2 *
    ************************************************** *************************)

    constructor TKlasse2.Create(Klasse1: TPointerAufKlasse1);
    begin
    Owner := Klasse1;
    end;

    procedure TKlasse2.ShowOwnerText;
    begin
    ShowMessage(Owner^.Text);
    end;

    end.

    </PRE>

    Der Aufruf würde dann so aussehen:

    <PRE>
    procedure TForm1.Button1Click(Sender: TObject);
    var
    MeineKlasse1 : TKlasse1;
    begin
    MeineKlasse1 := TKlasse1.Create('Test');
    MeineKlasse1.ShowOwnerText;
    MeineKlasse1.Free;
    end;
    </PRE>

    Für eine Erklärung wäre ich sehr dankbar.

    Andreas

  • #2
    Also bei mir funktioniert dein Code, sowohl mit als ohne Kommentar, und ich hab auf die
    Schnelle auch nichts entdeckt, weswegen er abstürzen sollte.
    <br>Aber ne Idee/Anmerkung zum Prinzip hab ich trotzdem: Den "TPointerAufKlasse1"
    kannst du dir IMHO sparen -- schreib stattdessen
    <pre>
    type
    TKlasse1 = class;
    TKlasse2 = class
    public
    Owner: TKlasse1;
    procedure ShowOwnerText;
    constructor Create(Klasse1: TKlasse1);
    end; { TKlasse1 benutzt TKlasse 2}

    constructor TKlasse2.Create(Klasse1: TKlasse1);
    begin
    Owner := Klasse1;
    end;
    </pre>
    und
    <pre>
    constructor TKlasse1.Create(Text: string);
    begin
    FText := Text;
    FSeite1 := TKlasse2.Create(Self);
    FSeite2 := TKlasse2.Create(Self);
    end;
    </pre>
    Das dürfte für deine Zwecke ausreichen, weil in "delphischen" Objektvariablen
    sowieso nur *Zeiger* auf die eigentlichen Instanzen stehen (auch wenn's
    vor dem Auge des Betrachters (leider, IMHO) gut versteckt ist).
    <br>(Du kannst ja spaßeshalber mal "Assert(Pointer(FSeite1.Owner) = Pointer(Self));" ans Ende von TKlasse1.Create schreiben und dir
    die beiden gecasteten Ausdrücke mal im Überwachungsfenster anschauen.)

    HTH, Uli

    Comment


    • #3
      Ich hab meinen Code nochmal zu Hause ausprobiert. Auch dort lief er nicht, wenn der Kommentar entfernt war. Die Adressen, auf die die Pointer zeigten hatte ich mir schon angesehen, sie standen auf der richtigen Adresse. Trotzdem war das Objekt bei Entfernen des Kommentars nicht mehr zugreifbar (selbst ein Vergleich auf nil führte zum Laufzeitfehler).

      Die von Dir vorgeschlagenen Änderungen habe ich ausprobiert, und damit funktioniert´s einwandfrei. Die geänderte Variante gefällt mir auch viel besser.

      Vielen Dank

      Andrea

      Comment


      • #4
        Hi

        Was Du tust ist gefährlich. Ich vermute mal Du kommst entweder von der C++ Fraktion oder bist noch zu sehr altes Object Pascal geschädigt.

        Also. Der Fehler liegt in Deiner <b>doppelten Referenzierung</b> eines Objectes. Das ist natürlich völlig sinnfrei.

        <pre>

        constructor XYZ.Create
        begin
        A := TA.Create(@Self);
        end;

        </pre>

        Schön, Self = Zeiger auf Object, und @Self = zeiger auf die Variable die den Zeiger enthält.
        </b>Wo wird Self gespeichert ?, und wie ist die Scope dieser unsichtbaren Variable definiert ?</b>
        Die Antwort ist einfach, @Self zeigt in den Stack und nachdem der Constructor() verlassen wurde ist dieser Stack ungültig geworden. D.h. Ptr = @Self -> Ptr^ zeigt zwar, aber auf irgendwas im Stack.<br>

        Noch gefählricher: Wird Ptr := Pointer(1234) gesetzt, also versucht die referenzierte Variable zu ändern überschreibt dies irgendwas anderes im Stack, z.B. den Caller o.ä.

        Fazit:
        <li>1. doppelte Refrenzierung ist unnötig da Delphi die normale Refrenzierungen transparent auflösst
        <li>2. verwende niemals eine Referenz auf procedure lokale Variablen deren Scope kürzer ist als die Lebensdauer der Referenz.

        Gruß hage

        Comment


        • #5
          So erklärt, ergibt das für mich jetzt auch einen Sinn.

          Danke für die Erklärung!

          Andreas

          (Die Vermutung mit der C++ Fraktion stimmt übrigens auch.

          Comment


          • #6
            &gt;&gt; (Die Vermutung mit der C++ Fraktion stimmt übrigens auch.)
            <br>
            Willkommen im Club! ;-)
            <br>Uli

            Comment


            • #7
              Schön zu sehen das auch andere Coder sich von der neuen Spache "Delphi" überzeugen lassen. Ich gebe zu einige Details könnte man wirklich noch aus C++ übernehmen, aber die Restriktionen in "Delphi" machen sich bei größeren, gewrateten Projekten schnell bezahlt.<br>
              Übrigens, ich betone "Programiersprache -> Delphi" und nicht "Object Pascal", da ich erschrocken feststellen musste das Borland von Anfang an Delphi als eigene Sprache versteht mit dem Ursprüngen in OP. Das die Hilfen von D1,D2,D3,D4,D5 und D6 eine "Object Pascal" Sprachbeschreibung enthalten ist laut Borland NUR ein Schussligkeitsfehler der unterprivilegierten Übersetzer gewesen

              Gruß Hage

              Comment


              • #8
                Na ja, hauptsächlich hab ich mich von der *IDE* Delphi und der
                Compilegeschwindigkeit überzeugen lassen. Und die Sprache
                (für mich bleibt's jetzt bei OP ;-)) ist zugegebenermassen
                meistens - nicht immer - lesbarer. Trotzdem tut's schon oft weh, wenn ich
                weiß "in C++ würdest du jetzt schnell die 5 Zeilen schreiben
                und fertig", während ich in OP "unsaubereren/unsichereren/mehr/ineffizienteren/..." Code
                schreiben muss

                Comment

                Working...
                X