Как правильно освободить массив объектов класса?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 08.01.2012 00:14:12

Код: Выделить всё
type
  TMyObject = class
    private
      ..
    public
      ...
    end;

type
  TMyArrayOfObjects = array of TMyObject;

...
...
var MyArr: TMyArrayOfObjects;
...
for i:=0 to ... do begin
  SetLength(MyArr,length(MyArr)+1);
  MyArr[high(MyArr)]:=TMyObject.Create;
  ...
...
// теперь массив больше не нужен, как его правильно убить?
MyArr:=nil;  так?
FreeAndNil() или так?
или Destroy для каждого элемента ?
или вообще SetLength(MyArr,0); ?
Последний раз редактировалось leo_bsv 09.01.2012 10:37:52, всего редактировалось 2 раз(а).
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение Brainenjii » 08.01.2012 00:38:40

Код: Выделить всё
Var
  i: Integer;
Begin
  For i := Low(MyArr) To High(MyArr) Do
    MyArr[i].Free;
  SetLength(MyArr, 0);
End;

Я бы сделал так. А ещё лучше - заменить массив на
Код: Выделить всё
..
  Uses
    ..,fgl,..
..
  Type TMyArray = Specialize TFPGList<TMyObject>;
..
Var
..
  MyArr: TMyArr;
..
Begin
..
  MyArr := TMyArr.Create;
  For i := 0 To Random(100) Do
    MyArr.Add(TMyObject.Create);
..
  For i := 0 To MyArr.Count - 1 Do
    MyArr[i].Free;
  MyArr.Free;
  ..
End;   
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Как правильно освободить массив объектов класса?

Сообщение MageSlayer » 08.01.2012 01:58:06

Так вроде в 2.6.0 появились шаблонные функции.
Вот это работа для них.

В общем Паскаль по-тихоньку превращается в нормальный функциональный язык :)
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 08.01.2012 02:02:28

Brainenjii писал(а):А ещё лучше - заменить массив на

круто! спасибо.
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение zub » 09.01.2012 00:55:30

>>А ещё лучше - заменить массив на
для массива классов смысла в этом никакого нет, только распухание кода. обычный tlist будет ничуть не хуже
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Как правильно освободить массив объектов класса?

Сообщение alexey38 » 09.01.2012 10:18:23

А не проще использовать TObjectList ?
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 09.01.2012 10:33:19

alexey38 писал(а):А не проще использовать TObjectList ?

а скажите пожалуйста, как работает метод
Код: Выделить всё
Function TObjectList.Remove(AObject: TObject): Integer;
begin
  Result:=Inherited Remove(Pointer(AObject));
end;

он ведь полностью сносит объект?
Никаких Distroy / nil / free к каждому элементу применять не надо?
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение Odyssey » 09.01.2012 11:13:33

Если у ObjectList'a свойство OwnsObjects = True, то сносит полностью, и самому освобождать память не надо.
Т.е. если в вашем коде есть одна из этих строк:
Код: Выделить всё
MyList := TObjectList.Create;
MyList := TObjectList.Create(True);
MyList.OwnsObjects := True;
то Destroy/Free для элементов MyList вызывает сам ObjectList.
А если в коде есть одна из следующих строк:
Код: Выделить всё
MyList := TObjectList.Create(False);
MyList.OwnsObjects := False;
то нужно вызывать Destroy/Free для элементов самому.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 09.01.2012 11:39:49

Odyssey писал(а):Т.е. если в вашем коде есть одна из этих строк:

А Clear тоже зависит от OwnsObjects? Или нет?
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение Odyssey » 09.01.2012 13:02:14

Да, зависит, как и Delete с Remove.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 09.01.2012 14:07:14

спасибо за развёрнутый ответ :!:

Добавлено спустя 1 час 3 минуты 16 секунд:
всё бы ничего... но использование свойств в таком виде:
Код: Выделить всё
TOdtTableColumnProperties(T.ColsProperties[i]).UseOptimalColumnWidth:=true;

по-моему извращение... :?
при более подробном рассмотрении решение от Brainenjii с использованием TFPGList является оптимальнм... и списком крутишь как хочешь и свойства класса вызываются напрямую...
вот так:
Код: Выделить всё
FColumns[nc].DefaultCellStyleName:=v;


Добавлено спустя 1 минуту 43 секунды:
Может быть я что-то упустил при работе с TObjectList? Может быть можно как-то удобно обращаться к свойствам входящих в него объектов?
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение alexey38 » 09.01.2012 19:51:41

leo_bsv писал(а):Может быть я что-то упустил при работе с TObjectList? Может быть можно как-то удобно обращаться к свойствам входящих в него объектов?

Я в таком случае для класса TObjectList делаю наследника с единственным методом типа CItem(Const index:integer):TMyClass; И при необходимости с соответствующим property.
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 09.01.2012 20:04:47

Код: Выделить всё
Я в таком случае для класса TObjectList делаю наследника с единственным методом типа CItem(Const index:integer):TMyClass; И при необходимости с соответствующим property.

вся фишка в том что я ничего не дописывал а только заменил TObjectList на Specialize TFPGList<TMyObject>; весь код при этом остался рабочим! после чего я затёр прямые преобразования типов вроде
TOdtTableColumnProperties(T.ColsProperties[i])
и код снова остался рабочим... в итоге накладные расходы на использование TFPGList ниже чем при TObjectList (методы практически все те же)... поправьте если не прав.
Дополнительное свойство CItem мне тоже не удобно использовать, т.к. само свойство - массив/список (как хотите обзовите)... и к нему уже должно через индексы обращаться...
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

Re: Как правильно освободить массив объектов класса?

Сообщение Brainenjii » 09.01.2012 20:11:38

Generic'и - класснючая вещь ^_^ Можно ещё написать свой Generic для списка - тогда будет и возможность сразу освобождать память на Delete, Remove и Clear с Free'ями. Вот мой вариант, правда требующий, чтобы у классов, конкретизирующих эти списки, обязательно присутствовало свойство ID
Код: Выделить всё
Unit BListsUnit;

{$mode objfpc}{$H+}

Interface

Uses
  Classes, SysUtils, syncobjs;

Type

{ BList }

  Generic BList<BManagedClass> = Class(TList)
  Private
    Function BListCompare(Item1, Item2: Pointer): Integer;

    Function GetItem(aIndex: Integer): BManagedClass;
    Procedure SetItem(aIndex: Integer; Const aValue: BManagedClass);

    Procedure BSort(aLeft, aRight: Integer);
  Public
    Property Item[aIndex: Integer]: BManagedClass
      Read GetItem Write SetItem; Default;

    Procedure Add(Const aValue: BManagedClass);
    Function SafeAdd(Const aValue: BManagedClass;
      Const aExceptionOnDuplicate: Boolean = FALSE): Boolean;
    Procedure Insert(Const aIndex: Integer; Const aValue: BManagedClass);
    Function Remove(Const aValue: BManagedClass): Integer;
    Procedure Delete(Const aIndex: Integer);
    Procedure Purge;

    Function Count: Integer;
    Function GetAt(Const aIndex: Integer): BManagedClass;
    Function Find(Const aID: Integer): BManagedClass;
    Function Present(Const aValue: BManagedClass): Boolean; Inline;

    Procedure Load(Const aList: BList);
    Procedure AddList(Const aList: TList);

    Procedure Sort;
    Procedure Compare(Const aTarget: BList; Out aSame, aOver, aLack: BList);

    Constructor Build;
    Destructor Burn;
End;

Type

{ BThreadList }

Generic BThreadList<BManagedClass> = Class
  Type BManagedList = Specialize BList<BManagedClass>;
  Protected
    bInternal: BManagedList;
    bSection: TCriticalSection;
  Public
    Procedure Add(Const aValue: BManagedClass);
    Procedure Remove(Const aValue: BManagedClass);
    Procedure Clear; Reintroduce;
    Procedure Purge;
    Procedure Load(Const aList:BManagedList);

    Function LockList: BManagedList;
    Procedure UnlockList;

    Constructor Build;
    Destructor Burn;
End;

Type

{ BLinkedList }

Generic BLinkedList<BManagedClass> = Class
  Type BFlatList = Specialize BList<BManagedClass>;
  Private
    bInternal: TList;
    Function GetFirst: BManagedClass; Inline;
    Function GetLast: BManagedClass; Inline;
  Public
    Property First: BManagedClass Read GetFirst;
    Property Last: BManagedClass Read GetLast;

    Procedure Add(Const aObject: BManagedClass);
    Procedure SafeAdd(Const aObject: BManagedClass); Inline;
    Procedure Insert(aObject: BManagedClass; Const aNext: BManagedClass = nil);

    Procedure Remove(Const aObject: BManagedClass);

    Function Present(Const aObject: BManagedClass): Boolean;

    Procedure Load(Const aList: BLinkedList);
    Function AsList: BFlatList;

    Constructor Build;
    Destructor Burn;
End;

Type

{ BLinkedThreadList }

Generic BLinkedThreadList<BManagedClass> = Class
  Type BManagedList = Specialize BLinkedList<BManagedClass>;
  Private
    bInternal: BManagedList;
    bSection: TCriticalSection;
  Public
    Procedure Add(Const aObject: BManagedClass);
    Procedure Load(Const aList: BManagedList);

    Procedure Remove(Const aObject: BManagedClass);
    Function LockList: BManagedList;
    Procedure UnlockList;

    Constructor Build;
    Destructor Burn;
End;

Implementation

{ BList }

Function BList.BListCompare(Item1, Item2: Pointer): Integer;
Begin
  If (Item1 = nil) Or (Item2 = nil) Then
    Begin
      If Item1 = nil Then
        If Not(Item2 = nil) Then Result := 1
        Else Result := 0
      Else
        Result := -1
    End
  Else
    Result := BManagedClass(Item2).ID - BManagedClass(Item1).ID;
End;

Function BList.GetItem(aIndex: Integer): BManagedClass;
Begin
  If Items[aIndex] = nil Then Raise Exception.Create('Definatly here!!!');
  Result := BManagedClass(Items[aIndex]);
end;

Procedure BList.SetItem(aIndex: Integer; Const aValue: BManagedClass);
Begin
  Items[aIndex] := aValue;
end;

Procedure BList.Add(Const aValue: BManagedClass);
Begin
  If aValue = nil Then
    Raise Exception.Create('Attempt of Nill adding');
  Inherited Add(aValue);
End;

Function BList.SafeAdd(Const aValue: BManagedClass;
  Const aExceptionOnDuplicate: Boolean): Boolean;
Var
  i: Integer;
  aPresent: Boolean;
Begin
  Result := FALSE;
  aPresent := FALSE;
  For i := 0 To Count - 1 Do
    If GetAt(i).Equals(aValue) Then
      Begin
        aPresent := TRUE;
        Break;
      End;
  If aPresent Then
    If aExceptionOnDuplicate Then
      Raise Exception.Create('Value already present in list')
    Else
      Exit;
  Add(aValue);
  Result := TRUE;
End;

Procedure BList.Insert(Const aIndex: Integer; Const aValue: BManagedClass);
Begin
  Inherited Insert(aIndex, aValue);
End;

Function BList.Remove(Const aValue: BManagedClass): Integer;
Begin
  Result := Inherited Remove(aValue);
End;

Procedure BList.Delete(Const aIndex: Integer);
Begin
  Inherited Delete(aIndex);
End;

Procedure BList.Purge;
Var
  i: Integer;
Begin
  For i := 0 To Count - 1 Do
    Item[i].Burn;
  Clear;
End;

Function BList.Count: Integer;
Begin
  Result := Inherited Count;
End;

Function BList.GetAt(Const aIndex: Integer): BManagedClass;
Begin
  If Items[aIndex] = nil Then Raise Exception.Create('HERE!');
  Result := BManagedClass(Items[aIndex]);
End;

Function BList.Find(Const aID: Integer): BManagedClass;
Var
  i: Integer;
Begin
  Result := nil;
  For i := 0 To Count - 1 Do
    If GetAt(i).ID = aID Then
      Begin
        Result := GetAt(i);
        Break;
      End;
End;

Function BList.Present(Const aValue: BManagedClass): Boolean;
Var
  i: Integer;
Begin
  Result := FALSE;
  For i := 0 To Count - 1 Do
    If GetAt(i).Equals(aValue) Then Exit(TRUE);
End;

Procedure BList.Load(Const aList: BList);
Begin
  Clear;
  AddList(aList);
End;

Procedure BList.BSort(aLeft, aRight: Integer);
Var
  i, j: Integer;
  aFirst, aBuffer: BManagedClass;
Begin
  Repeat
    i := aLeft;
    j := aRight;
    aFirst := Item[(aLeft + aRight) div 2];
    Repeat
      While Item[i].ID < aFirst.ID Do
        i += 1;
      While Item[j].ID > aFirst.ID Do
        j -= 1;
      If i <= j Then
        Begin
          aBuffer := Item[i];
          Item[i] := Item[j];
          Item[j] := aBuffer;
          i += 1;
          j -= 1;
        End;
    Until i > j;
    If aLeft < j Then BSort(aLeft, j);
    aLeft := i;
  Until Not(i < aRight);
End;

Procedure BList.AddList(Const aList: TList);
Begin
  Inherited AddList(aList);
End;

Procedure BList.Sort;
Begin
  If Count < 2 Then Exit;
  BSort(0, Count - 1);
End;

Procedure BList.Compare(Const aTarget: BList; Out aSame, aOver, aLack: BList);
Var
  i, j: Integer;
  aID, aTargetID: Integer;
Begin
  If aTarget = nil Then Raise Exception.Create('Illegal nil Target');

  Sort;
  aTarget.Sort;

  i := 0;
  j := 0;
  While TRUE Do
    Begin
      If i = Count Then aID := -1
      Else aID := GetAt(i).ID;
      If j = aTarget.Count Then aTargetID := -1
      Else aTargetID := aTarget.GetAt(j).ID;

      If (aID = -1) And (aTargetID = -1) Then Break;

      If aID = aTargetID Then
        Begin
          aSame.Add(GetAt(i));
          Inc(i);
          Inc(j);
        End
      Else
        Begin
          If ((aID < aTargetID) Or (aTargetID = -1)) And Not(aID = -1) Then
            Begin
              aOver.Add(GetAt(i));
              Inc(i)
            End;
          If ((aID > aTargetID) Or (aID = -1)) And Not(aTargetID = -1) Then
            Begin
              aLack.Add(aTarget.GetAt(j));
              Inc(j);
            End;
      End;
    End;
End;

Constructor BList.Build;
Begin
  Inherited Create;
End;

Destructor BList.Burn;
Begin
  Inherited Destroy;
End;

{ BThreadList }

Procedure BThreadList.Add(Const aValue: BManagedClass);
Begin
  bSection.Enter;
  bInternal.Add(aValue);
  bSection.Leave;
End;

Procedure BThreadList.Remove(Const aValue: BManagedClass);
Begin
  bSection.Enter;
  bInternal.Remove(aValue);
  bSection.Leave;
End;

Procedure BThreadList.Clear;
Begin
  bSection.Enter;
  bInternal.Clear;
  bSection.Leave;
End;

Procedure BThreadList.Purge;
Var
  i: Integer;
Begin
  bSection.Enter;
  For i := 0 To bInternal.Count - 1 Do
    bInternal.GetAt(i).Burn;
  bInternal.Clear;
  bSection.Leave;
End;

Procedure BThreadList.Load(Const aList: BManagedList);
Begin
  bSection.Enter;
  bInternal.Load(aList);
  bSection.Leave;
End;

Function BThreadList.LockList: BManagedList;
Begin
  bSection.Enter;
  Result := bInternal;
End;

Procedure BThreadList.UnlockList;
Begin
  bSection.Leave;
End;

Constructor BThreadList.Build;
Begin
  bSection := TCriticalSection.Create;
  bInternal := BManagedList.Build;
End;

Destructor BThreadList.Burn;
Begin
  bInternal.Burn;
  bSection.Free;
End;

{ BLinkedList }

Function BLinkedList.GetFirst: BManagedClass;
Var
  i: Integer;
Begin
  Result := nil;
  For i := 0 To bInternal.Count - 1 Do
    If BManagedClass(bInternal[i]).Prior = nil Then
      Exit(BManagedClass(bInternal[i]));
end;

Function BLinkedList.GetLast: BManagedClass;
Var
  i: Integer;
Begin
  Result := nil;
  For i := 0 To bInternal.Count - 1 Do
    If BManagedClass(bInternal[i]).Next = nil Then
      Exit(BManagedClass(bInternal[i]));
end;

Procedure BLinkedList.Add(Const aObject: BManagedClass);
Begin
  bInternal.Add(aObject);
End;

Procedure BLinkedList.SafeAdd(Const aObject: BManagedClass);
Begin
  If Not(Present(aObject)) Then Add(aObject);
End;

Procedure BLinkedList.Insert(aObject: BManagedClass;Const aNext: BManagedClass);
Var
  aIndex: Integer;
Begin
  If aObject = nil Then Raise Exception.Create('Nil insertion attempted');
  If (aNext = nil) Or (bInternal.Count = 0) Then Add(aObject)
  Else
    Begin
      aIndex := bInternal.IndexOf(aNext);
      If aIndex = -1 Then bInternal.Add(aObject)
      Else bInternal.Insert(aIndex, aObject);
    End;
End;

Procedure BLinkedList.Remove(Const aObject: BManagedClass);
Begin
  If Not(aObject.Prior = nil) Then aObject.Prior.Next := aObject.Next;
  If Not(aObject.Next = nil) Then aObject.Next.Prior := aObject.Prior;
  bInternal.Remove(aObject);
End;

//Procedure BLinkedList.Delete(Const aIndex: Integer);
//Begin
//
//End;
//
Function BLinkedList.Present(Const aObject: BManagedClass): Boolean;
Begin

End;

Procedure BLinkedList.Load(Const aList: BLinkedList);
Begin
  bInternal.Clear;
  bInternal.AddList(aList.bInternal);
End;

Function BLinkedList.AsList: BFlatList;
Begin
  Result :=  BFlatList(bInternal);
End;

Constructor BLinkedList.Build;
Begin
  bInternal := TList.Create;
End;

Destructor BLinkedList.Burn;
Begin
  bInternal.Free;
End;

{ BLinkedThreadList }

Procedure BLinkedThreadList.Add(Const aObject: BManagedClass);
Begin
  bSection.Enter;
  bInternal.Add(aObject);
  bSection.Leave;
End;

Procedure BLinkedThreadList.Load(Const aList: BManagedList);
Begin
  bSection.Enter;
  bInternal.Load(aList);
  bSection.Leave;
End;

Procedure BLinkedThreadList.Remove(Const aObject: BManagedClass);
Begin
  bSection.Enter;
  bInternal.Remove(aObject);
  bSection.Leave;
End;

Function BLinkedThreadList.LockList: BManagedList;
Begin
  bSection.Enter;
  Result := bInternal;
End;

Procedure BLinkedThreadList.UnlockList;
Begin
  bSection.Leave;
End;

Constructor BLinkedThreadList.Build;
Begin
  bSection := TCriticalSection.Create;
  bInternal := BManagedList.Build;
End;

Destructor BLinkedThreadList.Burn;
Begin
  bInternal.Burn;
  bSection.Free;
End;

End.

А на кодогенератор - так вообще надышаться не могу - насколько он офигенный вышел ^_^
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Как правильно освободить массив объектов класса?

Сообщение leo_bsv » 09.01.2012 20:53:30

Brainenjii писал(а):Generic'и - класснючая вещь ^_^

+1 избавляет от кучи проблем...
Brainenjii писал(а):А на кодогенератор - так вообще надышаться не могу

какое-то слово страшное... ты чай пьёшь - он кодит? :wink: что он такое делает? (извините недалёкого) :D
Аватара пользователя
leo_bsv
постоялец
 
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола

След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 230

Рейтинг@Mail.ru
cron