Page 1 of 2 1 2 LastLast
Results 1 to 15 of 20

Thread: Een object een ander object laten verwijderen lukt niet

  1. #1
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106

    Een object een ander object laten verwijderen lukt niet

    Hallo,

    Ik heb een object TTekstStijl die een simpele Decorator moet voorstellen.
    En die maakt hoofdletters van een string en dat werkt goed

    Ik geef een TTekst mee en die moet hij als hij klaar is verwijderen.
    Maar in de aanroepende unit waar de TTekst gecreated is blijft de TTekst op Assigned staan.
    Doe ik in de aanroepende unit FreeAndNil(oTekstMetStijl.FWrapped); Dan is hij wel weg.
    Doe ik in de TTekstStijl class FreeAndNil(FWrapped); Dan blijft hij assigned en dat wil ik niet.

    In de constructor wordt FWrapped gelijk gemaakt aan Wrapped, maar ze blijken toch niet helemaal hetzelfde.

    Hoe krijg ik het voor elkaar dat ze wel hetzeflde zijn?
    Zodat ik het Wrapped object kan wissen vanuit deze class.

    Wat ik dus wil is dat de decorator het gedecoreerde object wist nadat hij het gebruikt heeft (Ik snap dat dit stom klinkt maar wil toch weten waarom het wissen niet lukt)

    Interface:
    Code:
    TTekst = class(TObject)
    public
      function GeefTekst:string; virtual;
    end;
    
    TTekstStijl = class(TTekst)
    public
      FWrapped: TTekst;
    public
      constructor Create(Wrapped: TTekst); reintroduce;
      destructor Destroy; override;
      function GeefTekst:string; override;
      function GeefTekstHL: string; virtual;
    end;
    Implementation:
    Code:
    function TTekst.GeefTekst: string;
    begin
      Result := 'Hallo';
    end;
    
    constructor TTekstStijl.Create(Wrapped: TTekst);
    begin
      inherited Create;
      FWrapped := Wrapped; //<= Hier wordt FWrapped gelijk gemaakt aan Wrapped, maar ze zijn blijkbaar niet echt hetzelfde. Hoe krijg ik het voor elkaar dat ze wel hetzeflde zijn?
    end;                              //      Zodat ik het Wrapped object kan wissen vanuit deze class.
    
    destructor TTekstStijl.Destroy;
    begin
      FreeAndNil(FWrapped); //<== Hier moet hij gefreed worden, maar hij blijft in de aanroepende unit gewoon op Assigned staan
      inherited Destroy;
    end;
    
    function TTekstStijl.GeefTekst: string;
    begin
    
      Result := FWrapped.GeefTekst;
    end;
    
    function TTekstStijl.GeefTekstHL: string;
    begin
      Result := Geeftekst.ToUpper;
    end;
    Dit is de aanroepende code
    Code:
    procedure TForm1.Button1Click(Sender: TObject);
    var
      oTekst: TTekst;
       oTekstMetStijl: TTekstStijl;
    begin
      oTekst := TTekst.Create;
      ShowMessage(oTekst.GeefTekst);
    
      oTekstMetStijl := TTekstStijl.Create(oTekst);
      ShowMessage(oTekstMetStijl.GeefTekstHL);
      FreeAndNil(oTekstMetStijl);
      // FreeAndNil(oTekstMetStijl.FWrapped); //<=Als ik dit doe dan is hij uiteraard wel echt weg, terwijl het om dezelfde variabele gaat.
      if Assigned(oTekst) then
      begin
        ShowMessage('oTekst bestaat nog'); //<==oTekst mag niet meer bestaan op dit punt
      end;
    end;

  2. #2
    Je oTekst is volgens mij wel vrijgegeven. Alleen is de pointer waar oTekst naar verwijst niet op nul gezet. Daarom werkt assigned ook niet. (dat zou met oTekst.Free overigens ook zo zijn geweest) Je kunt dit controleren door de memoryleakdetector aan te zetten. Dan krijg je een foutmelding bij het afsluiten van je programma over de niet vrijgegeven objecten.

    Dit opnemen in je .dpr
    Code:
    {$IFDEF DEBUG}
        ReportMemoryLeaksOnShutdown := True;
    {$ENDIF}
    Maar waarom wil je met assigned(oTekst) iets doen? Je weet dat ie na de destroy van oTekstMetStyle niet meer bestaat.

    (Soms is het ook beter om niet teveel met FreeAndNil te werken. Dat wijst n.l. vaak op ondoordachte code. Je moet FreeAndNil alleen gebruiken als het echt noodzakelijk is dat de pointer op nul gezet moet worden.)

  3. #3
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Quote Originally Posted by rvk View Post
    Je oTekst is volgens mij wel vrijgegeven. Alleen is de pointer waar oTekst naar verwijst niet op nul gezet. Daarom werkt assigned ook niet. (dat zou met oTekst.Free overigens ook zo zijn geweest) Je kunt dit controleren door de memoryleakdetector aan te zetten. Dan krijg je een foutmelding bij het afsluiten van je programma over de niet vrijgegeven objecten.

    Dit opnemen in je .dpr
    Code:
    {$IFDEF DEBUG}
        ReportMemoryLeaksOnShutdown := True;
    {$ENDIF}
    Maar waarom wil je met assigned(oTekst) iets doen? Je weet dat ie na de destroy van oTekstMetStyle niet meer bestaat.

    (Soms is het ook beter om niet teveel met FreeAndNil te werken. Dat wijst n.l. vaak op ondoordachte code. Je moet FreeAndNil alleen gebruiken als het echt noodzakelijk is dat de pointer op nul gezet moet worden.)
    Bedankt voor je bericht. Ik kreeg geen memoryleak omdat het object nog steeds bestaat.


    Ik gebruik in destructor
    Code:
    TTekstStijl.Destroy;
    begin
      FreeAndNil(FWrapped);
      inherited Destroy;
    end;
    En ik zoek een manier zodat FWrapped echt weg is, als ik hem op nil zou zetten dan krijg ik memoryleak.
    Het probleem zit hem in
    Code:
    constructor TTekstStijl.Create(Wrapped: TTekst);
    begin
      inherited Create;
      FWrapped := Wrapped; 
    end;
    Bij FWrapped := Wrapped zouden ze echt gelijk moeten zijn, maar dat zijn ze niet daarom blijft Wrapped wel geassigned en FWrapped niet, het moet tock kunnen dat ik een Wrapped echt weg wil hebben.

    Maar waarom wil je met assigned(oTekst) iets doen? Je weet dat ie na de destroy van oTekstMetStyle niet meer bestaat.
    Hij bestaat nog wel en dat is precies het eigenaardige waarom ik dit topic ben begonnen.

  4. #4
    Quote Originally Posted by ActiveS View Post
    Ik kreeg geen memoryleak omdat het object nog steeds bestaat.
    Nee. Je krijgt juist een melding als ie wel zou bestaan bij het beëindigen van je programma. Dan heb je een leak. Nu je dus geen melding krijgt betekent dat dus dat ie dus echt wel opgeruimd is.

    Quote Originally Posted by ActiveS View Post
    En ik zoek een manier zodat FWrapped echt weg is, als ik hem op nil zou zetten dan krijg ik memoryleak.
    Hij is met deze FreeAndNil echt weg.
    Alleen verwijst je oorspronkelijke oTekst nog naar een VERWIJDERD object. Die kun en mag je dus niet opnieuw gebruiken. Het gaat alleen om de POINTER die nog bestaat. Niet om het object zelf. Er is dus een groot verschil tussen het object zelf (memory/object) en de pointer naar dat stukje geheugen.

    Quote Originally Posted by ActiveS View Post
    Bij FWrapped := Wrapped zouden ze echt gelijk moeten zijn, maar dat zijn ze niet daarom blijft Wrapped wel geassigned en FWrapped niet, het moet tock kunnen dat ik een Wrapped echt weg wil hebben.
    Nee, je FWrapped en Wrapped verwijzen met een pointer naar hetzelfde object. Als je FWrapped.Free doet (in destroy) dan ruim je wel het object op. Met FreeAndNil(FWrapped) zet je ook FWrapped op nil. Maar Wrapped verwijst nog steeds naar het verwijderde object. Je zou dan dus Wrapped := nil kunnen doen. Je kunt dat echter niet in destroy doen. Maar je zou het wel in create kunnen doen als je van Wrapped een var maakt.

    Zoiets
    Code:
    constructor TTekstStijl.Create(var Wrapped: TTekst);
    begin
      inherited Create;
      FWrapped := Wrapped;
      wrapped := nil;
    end;
    Maar nogmaals.. dit zou bij juist programming niet nodig mogen zijn (zelfs slordig) omdat je met oTekst in je aanroep code toch niets meer doet.

    Ps. Je ruimt overigens normaal oTekst ook op in de aanroepende routine met een try/finally constructie zodat als TTekstStyle.Create zou mislukken je geen memoryleak hebt. Dat zou je met deze constructie wel hebben.

    Quote Originally Posted by ActiveS View Post
    Hij bestaat nog wel en dat is precies het eigenaardige waarom ik dit topic ben begonnen.
    Nee, hij bestaat echt niet meer ondanks dat die pointer oTekst assigned blijft. Assigned betekend NIET dat er een geldig object achterhangt.

  5. #5
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Nee, hij bestaat echt niet meer ondanks dat die pointer oTekst assigned blijft. Assigned betekend NIET dat er een geldig object achterhangt.
    Je hebt gelijk, maar ik kan nog wel GeefTekst aanroepen.
    Maak ik van TTekst een TPanel dan kan kan ook GeefTekst aanroepen, alleen de Parent kan ik niet instellen. (dat heb ik geprobeerd, dan krijg ik een access violation).


    Het lijkt net of oTekst nog half bestaat. Want dit werkt:

    Code:
    if Assigned(oTekst) then
      begin
        ShowMessage('oTekst bestaat nog');
        ShowMessage(oTekst.GeefTekst);
      end;
    Als ik van oTekst een TPanel maak dan gaat het inderdaad fout bij de parent toekenning.

    Code:
     
    if Assigned(oTekst) then
      begin
        ShowMessage('oTekst bestaat nog');
        ShowMessage(oTekst.GeefTekst);
        oTekst.parent := self;
      end;
    Wat ik jammer vind is dat er dan geen goede manier mogelijk is om te checken of een object nog wel helemaal bestaat.

  6. #6
    Quote Originally Posted by ActiveS View Post
    Je hebt gelijk, maar ik kan nog wel GeefTekst aanroepen.
    Dat klopt. Maar is zeer zeeer zeeer gevaarlijk. Want je roep in feite een object aan wat niet meer bestaat. Als het geheugen opnieuw gebruikt wordt voor een ander object kun je ook lekkere crashes krijgen.

    Quote Originally Posted by ActiveS View Post
    Wat ik jammer vind is dat er dan geen goede manier mogelijk is om te checken of een object nog wel helemaal bestaat.
    Nee. Die is er niet. Zie ook dit antwoord https://stackoverflow.com/a/33319401/1037511

    In general, it is not possible to make a robust test that a pointer refers to an instance that has been freed or not. It is the job of the programmer to maintain control of the lifetime of your objects.
    Je kunt hem inderdaad op nil zetten in create, zoals ik liet zien, maar beter is het om hem netjes via een try/finally netjes op te ruimen. Zoals het hoort.

    Als je moet weten of er nog een object in staat dan is je code in 99% van de gevallen niet juist/logisch.

  7. #7
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Bedankt!

    Code:
    constructor TTekstStijl.Create(var Wrapped: TTekst);
    begin
      inherited Create(nil);
      FWrapped := Wrapped;
      Wrapped  := nil;
    end;
    werkt goed in de Create. Het Assigned statement werkt nu ook zo ik wilde.

    Dat van try finally blok is inderdaad beter. Ik heb oTekst buiten de oTekstMetStijl gecreate, dus dan is het ook handig om ook daarbuiten te freeen.

    Ook bedankt voor de link.

  8. #8
    Het 'probleem' is dat je meerdere verwijzingen hebt naar hetzelfde object. Als je een variabele nil maakt, dan maak je alleen die verwijzing nil. Als je een object vrijgeeft, dan is het object weg, al zijn de verwijzingen er nog.

    Vergelijk het met een telefoonboek (ik hoop dat je ouder dan 30 bent ). Als jij en ik allebei een telefoonboek hebben met Marcel's adres er in, dan heeft Marcel nog steeds maar één adres. Als ik mijn telefoonboek verbrand, heb jij nog steeds jouw telefoonboek. Als ik mijn telefoonboek en Marcel's huis verbrand, heb jij nog steeds een telefoonboek, maar nu met een niet bestaand adres erin.

    Je laatste oplossing verschuift het probleem alleen maar. Als ik deze aanroepende code schrijf, dan zit je nog steeds met references die niet nil zijn. Jouw telefoonboek B is verbrand, maar onderin de la lag nog telefoonboek A...
    Code:
    var
      A, B: TTekst;
    begin
      A := TTekst.Create;
      B := A;
      TTekstStijl(B);
      if Assigned(A) then
        ShowMessage('Helaas, nog steeds assigned').
    end;
    1+1=b

  9. #9
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Het 'probleem' is dat je meerdere verwijzingen hebt naar hetzelfde object.
    Ja dat is het ook. Maar ik weet geen manier om deze dubbele variabelen te voorkomen.
    Ik dacht aanvankelijk dat als je Wrapped op nil zet dat dan FWrapped ook gelijk nil zou zijn, maar dat bljikt dus anders te zijn.

    Ik had gewild dat FWrapped exact gelijk was aan Wrapped, zodat als ik FreeAndNil(FWrapped) doe dat Wrapped ook gelijk op nil komt te staan.

    Dit topic leert me dat dat anders is.

  10. #10
    waarom niet ttekst oftwel FWrapped create-en zodra je TTekstStijl.Create doet? en dus oTekst niet meer doen. Daarmee is het meteen duidelijk wie de eigenaar is van die FWrapped /ttekst en dus ook verantwoordelijk is voor het opruimen?

  11. #11
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Quote Originally Posted by Miep View Post
    waarom niet ttekst oftwel FWrapped create-en zodra je TTekstStijl.Create doet? en dus oTekst niet meer doen. Daarmee is het meteen duidelijk wie de eigenaar is van die FWrapped /ttekst en dus ook verantwoordelijk is voor het opruimen?
    Helemaal mee eens zo ga ik het ook doen.

  12. #12
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Even inhaken in dit geval
    Delphi Code:
    1. TTekst = class(TObject)
    2. public
    3.   function GeefTekst:string; virtual;
    4. end;
    5.  
    6. TTekstStijl = class(TTekst)
    7. public
    8.   FWrapped: TTekst;
    9. public
    10.   constructor Create(Wrapped: TTekst); reintroduce;
    11.   destructor Destroy; override;
    12.   function GeefTekst:string; override;
    13.   function GeefTekstHL: string; virtual;
    14. end;
    In dit geval is class TTekst toch onderdeel van TTekststijl? Waarom zal je dan een override functie maken van Geeftekst?
    Class TTekst heeft hier helemaal geen functie meer. Als je toch gebruik wilt maken van class TTekst in meerdere objecten, haal dan FWrapped helemaal weg.
    Delphi Code:
    1. TTekst = class  
    2. public
    3.   function GeefTekst:string;
    4. end;
    5.  
    6. TTekstStijl = class(TTekst)
    7. private
    8.   fValue : string;
    9. public
    10.   constructor Create;
    11.   destructor Destroy; override;
    12.   function GeefTekstHL: string;
    13. end;
    14.  
    15. implenmentation
    16.  
    17. procedure CreateHuisstel;
    18. var fstijl1 : TTekstStijl;
    19.       fstijl2 : TTekstStijl;
    20. begin
    21.   fstijl1 := TTekstStijl.create;
    22.   fstijl2 := TTekstStijl.create;
    23.    try
    24.       fstijl1.fValue := 'welkom';
    25.       fstijl2.value := 'bij nldelphi';
    26.       showmessage(fstijl1.Geeftekst);
    27.       showmessage(fstijl2.Geeftekst);
    28.    finally
    29.      fstijl1.free;
    30.      fstijl2.free;
    31.    end;
    32. end;

    (Waarschijnlijk bedoelt Goleztrol dit ook, maar kon het niet goed uithalen ;D)
    Delphi is great. Lazarus is more powerfull

  13. #13
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Hallo jkuiper,

    Ik heb een boek over Design Patterns voor Delphi en er staat een voorbeeld in hoe een Decorator werk en daar had ik mijn code op gebasseerd, dat riep bij mij vragen op vandaar dat ik naar het NLDelphi forum ben gegaan.
    De code van jou is geen decorator meer, maar meer de "gewone" manier van programmeren. Als ik ongelijk heb dan hoor ik het graag hoor.

  14. #14
    Zover ik decorator snap had voor een decorator de regel
    Code:
    oTekstMetStijl := TTekstStijl.Create(oTekst);
    geschreven moeten worden als
    Code:
    oTekst := TTekstStijl.Create(oTekst);

  15. #15
    Senior Member
    Join Date
    Aug 2003
    Location
    Vlaardingen
    Posts
    106
    Quote Originally Posted by Miep View Post
    Zover ik decorator snap had voor een decorator de regel
    Code:
    oTekstMetStijl := TTekstStijl.Create(oTekst);
    geschreven moeten worden als
    Code:
    oTekst := TTekstStijl.Create(oTekst);
    Wat ik er van begrijp is dat je het te decoreren object meegeeft aan de creator
    en vervolgens voer je van de decorator de method uit die de daadwerkelijke decoratie uitvoert, in dit geval GeefTekstHL.

    Code:
    oTekstMetStijl := TTekstStijl.Create(oTekst);
      ShowMessage(oTekstMetStijl.GeefTekstHL);

Page 1 of 2 1 2 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •