Samoniszczące się obiekty i ich lista

0

Siedzę nad tym jakiś czas i co bym nie wymyślił to nie działa tak jak chcę. A chcę aby było tak:

Mam listę obiektów (TList), tworzę obiekt i dodaję go do listy. Obiekt sobie żyje swoim własnym życiem, aż w pewnym momencie w jednym z jego zdarzeń chcę go usunąć, bo przestał być potrzebny. Przy okazji, żeby usunął się z tej listy. Najgorsze jest to, że wszelkie akcje na tych obiektach są podejmowane w pętli po liście, więc jak obiekt sam siebie zniszczy i wypisze z listy to psuje się pętla.

TObiekt = class;

TMenedzerObiektow = class
private
  FObiekty: TList;
  procedure DodajObiekt(AObiekt: TObiekt);
  procedure UsunObiekt(AObiekt: TObiekt);
public
  constructor Create;
  destructor Destroy; override;
  procedure RobCos;
end;
  
TObiekt = class
private
  FOwner: TMenedzerObiektow;
public
  constructor Create(AOwner: TMenedzerObiektow);
  destructor Destroy; override;
  procedure RobCos;
end;

constructor TMenedzerObiektow.Create;
begin
  FObiekty := TList.Create;
end;

destructor TMenedzerObiektow.Destroy;
var
  n: Integer;
begin
  for n := 0 to FObiekty.Count - 1 do
    TObiekt(FObiekty[n]).Free;
  FObiekty.Free;
  inherited;
end;

procedure TMenedrzeObiektow.DodajObiekt(AObiekt: TObiekt);
begin
  FObiekty.Add(AObiekt);
end;

procedure TMenedrzeObiektow.UsunObiekt(AObiekt: TObiekt);
begin
  FObiekty.Remove(AObiekt);
end;

procedure TMenedrzeObiektow.RobCos;
var
  n: Integer;
begin
  for n := 0 to FObiekty.Count - 1 do
    TObiekt(FObiekty[n]).RobCos;
end;

constructor TObiekt.Create(AOwner: TMenedzerObiektow);
begin
  FOwner := AOwner;
  FOwner.DodajObiekt(Self);
end;

destructor TObiekt.Destroy;
begin
  inherited;
  FOwner.UsunObiekt(Self);  
end;

procedure TObiekt.RobCos;
begin
  // coś tam tutaj robię i w pewnym momencie chcę usunąć ten obiekt
  // załóżmy, że odpowiada za to "Warunek"
  if Warunek then Self.Free;
end;

W momencie usunięcie elementu z listy rozwala się pętla i wywala out of bounds, wiadomo. Próbowałem oznaczać obiekt do usunięcia poprzez zmienną i po pętli z RobCos sprawdzałem kolejną pętlą, ale od ostatniego do pierwszego czy obiekt jest oznaczony i wtedy go usuwałem (wtedy usunięcie elementu nie wpływa na resztę listy, której pętla nie objęła), ale działy się dziwne rzeczy :|

Ma ktoś jakiś pomysł na taką listę?

0

Możesz np. zamienić TObiekt.RobCos na funkcję, która zwróci false, jeśli będziesz chciał usunąć obiekt. Wtedy:

   Procedure TMenedzerObiektow.RobCos;
    Var
     N : Integer;
     C : Integer;

   Begin
    N:=0;
    C:=FObiekty.Count;
     While (N<C) Do
           If TObiekt(FObiekty[N]).RobCos Then
              Inc(N)
           Else
              Begin
               TObiekt(FObiekty[N]).Free;
               Dec(C);
              End;
   End;

Jeżeli to nie jest wyjście dla Ciebie to możesz tak:

   Procedure TMenedzerObiektow.RobCos;
    Var
     N : Integer;
     C : Integer;

   Begin
    N:=0;
    C:=FObiekty.Count;
     While (N<C) Do
           Begin
            TObiekt(FObiekty[N]).RobCos;
              { Przy założeniu, że metoda powyżej usunęła co najwyżej jeden obiekt (sama siebie) }
             If (FObiekty.Count=C) Then { Niezbyt szybkie - można wprowadzić jakąś flagę ustawianą }
                                        { w momencie usuwania obiektu i stosowną funkcję *UsunietoElement* }
                Inc(N)
             Else
                Dec(C);
           End;
   End;

Wszystko zależy od tego co chcesz osiągnąć. Czy np. dany TObiekt będzie niszczył wyłącznie sam siebie czy może także inne obiekty z listy?

0

sprobuj zamiast petli

"for n := 0 to FObiekty.Count - 1 do"

zmianic na

"for n := FObiekty.Count - 1 downto 0 do"

na 99% powinno rozwiazac problem

0

W sumie to tak, ale to mimo wszystko jakieś dziwy* wychodzą... albo mam jakiegoś buga (ale podczas działania i operacji na setkach obiektów, które co chwila tworzone są nowe i stare usuwane to wygląda wszystko ok - licznik dobrze wskazuje i nie ma żadnych access violationów, etc)

Czy na pewno obiekt może sam siebie zniszczyć poprzez Self.Free?

  • Dziwy = np. obiekt z grupy 1 zmienia swoją wartość A, natomiast obiekt z grupy 2 powinien tylko wartość B, a wychodzi, że zmienia A i B jednocześnie :| Dzieje się tak dopiero po tym jak kilka obiektów z grupy 1 sama się zniszczy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1