Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.10.11;
Скачать: CL | DM;

Вниз

TList & records   Найти похожие ветки 

 
_Андрей   (2009-08-07 12:14) [0]

Здравствуйте.

Для хранения списка таких вот записей:

type
 PRasEntry = ^TRasEntry;
 TRasEntry = record
 Name, User, Pswd, Phone: string;
end;


создал наследника TList:

type TRasList = class(TList)
 private
   function ReadItem(index: integer): TRasEntry;
   procedure WriteItem(index: integer; const Value: TRasEntry);
 public
   procedure AddItem(RasEntry: TRasEntry); overload;
   procedure AddItem(Name, User, Pswd, Phone: string); overload;
   property RasItems[index: integer]: TRasEntry read ReadItem write WriteItem;
   destructor Destroy; override;
end;

Вот его реализация:

procedure TRasList.AddItem(RasEntry: TRasEntry);
var P: PRasEntry;
begin
 New(P);
 P^ := RasEntry;
 Add(P)
end;

procedure TRasList.AddItem(Name, User, Pswd, Phone: string);
var P: PRasEntry;
begin
 New(P);
 P^.Name := Name; P^.User := User; P^.Pswd := Pswd; P^.Phone := Phone;
 Add(P)
end;

function TRasList.ReadItem(index: integer): TRasEntry;
begin
 if (index >=0) and (index <= Count-1) then Result := TRasEntry(Items[index]^)
end;

procedure TRasList.WriteItem(index: integer; const Value: TRasEntry);
begin
 if (index >=0) and (index <= Count-1) then
   begin
   TRasEntry(Items[index]^).Name := Value.Name;
   TRasEntry(Items[index]^).User := Value.User;
   TRasEntry(Items[index]^).Pswd := Value.Pswd;
   TRasEntry(Items[index]^).Phone := Value.Phone
   end
end;

destructor TRasList.Destroy;
var i: integer;
begin
 for i:=Count-1 downto 0 do
   begin
   Dispose(Items[i]);
   Delete(i)
   end;
 inherited
end;


Проблема в том что FastMM при добавлении каких-либо элементов в список находит в этом коде утечки памяти, подскажите, плиз, что не так делаю, может у кого-нить есть пример хранения записей в списке...


 
Kolan ©   (2009-08-07 12:27) [1]

Интересно, почему вы не использовали объекты?


 
_Андрей   (2009-08-07 12:35) [2]


> Kolan ©   (07.08.09 12:27) [1]
> Интересно, почему вы не использовали объекты?


В смысле? О каких объектах речь?


 
DVM ©   (2009-08-07 12:46) [3]


> Проблема в том что FastMM при добавлении каких-либо элементов
> в список находит в этом коде утечки памяти, подскажите,
> плиз, что не так делаю


> Dispose(Items[i]);

замени на

Dispose(PRasEntry (Items[i]));


 
Игорь Шевченко ©   (2009-08-07 12:47) [4]


> Dispose(Items[i]);


Dispose(PRasEntry(Items[i]))


 
Юрий Зотов ©   (2009-08-07 12:58) [5]

> _Андрей   (07.08.09 12:35) [2]

В деструкторе (точнее, в Dispose), чтобы правильно освободить память и финализировать строки записей нужно явно задать тип указателя (см. [3] и [4]). Иначе используется нетипизированный указатель (поскольку в TList они и есть) и строки записей могут "повиснуть".

А проще было бы вместо record использовать class, а вместо TList - TObjectList.


 
Юрий Зотов ©   (2009-08-07 13:00) [6]

Вдогонку - и если этот class наследовать от TCollectionItem, то вместо списка можно использовать коллекцию. Что лучше - зависит от задачи.


 
_Андрей   (2009-08-07 13:03) [7]

Ага), больше FastMM не ругается, здорово, большущее спасибо..!
А в чём была проблема, понятно в опщем что в 1-м случае освобождалась не вся память, но вот что конкретно дало приведение типа (PRasEntry(Items[i]))? Объясните, если нетрудно, несведущему...


 
_Андрей   (2009-08-07 13:07) [8]


> Юрий Зотов ©   (07.08.09 12:58) [5]


А как Dispose узнаёт "сколько" памяти необходимо освободить?


> А проще было бы вместо record использовать class, а вместо
> TList - TObjectList.


Мне кажеться это здесь лишнее, записей здесь более чем достаточно, 4 строчки и всё, вот если бы объект был посложнее...


 
Медвежонок Пятачок ©   (2009-08-07 13:13) [9]

Ну так здесь и сам наследник TList тоже лишний.
Хватит самого тлиста и обычной процедуры ClearMyList


 
MBo ©   (2009-08-07 13:15) [10]

>А как Dispose узнаёт "сколько" памяти необходимо освободить?
это "умная" процедура, ей неявно передается информация о типе, и она до освобождения непосредственно выделенной памяти (в данном случае 16 байт под указатели на строки) выполняет Finalize для корректного освобождения (финализации) динамических типов


 
_Андрей   (2009-08-07 13:18) [11]


> Медвежонок Пятачок ©   (07.08.09 13:13) [9]


Наследний был нужен для того, чтобы добавить процедуры добавления новых записей, так намного удобней (много где потом это используется), в опщем лучше здесь сделать здесь чуть больше чтоб потом в самой проге код не захламлять, достаточно написать, например:

RasList.AddItem("mts", "mts", "mts", "pw8w9707070707") и всё...


 
_Андрей   (2009-08-07 13:20) [12]


> MBo ©   (07.08.09 13:15) [10]


А что тогда делает:

Dispose(Items[i])

Осовбождает только часть или вообще ничего?


 
MBo ©   (2009-08-07 13:23) [13]

Dispose(Items[i]) передается нетипизированный указатель, и про него системе известно только то, что по этому адресу выделено 16 байт


 
Юрий Зотов ©   (2009-08-07 13:24) [14]

> _Андрей   (07.08.09 13:20) [12]

Освобождает память, занятую записью, но не освобождает память, занятую строками этой записи (name и пр.).


 
Юрий Зотов ©   (2009-08-07 13:28) [15]

> _Андрей   (07.08.09 13:20) [12]

Длиные строки Delphi - это указатели. Поэтому Ваша запись содержит 4 указателя и занимает 16 байт, независимо от длины ее строк. Сами же строки (то есть, их тела) хранятся в другом месте.


 
Best of the best   (2009-08-07 13:29) [16]


> destructor TRasList.Destroy;
> var i: integer;
> begin
>  for i:=Count-1 downto 0 do
>    begin
>    Dispose(Items[i]);
>    Delete(i)
>    end;
>  inherited
> end;
>



Вместо вышеописанного правильнее будет перекрыть метод Notify:

 
protected
   procedure Notify(Ptr: Pointer; Action: TListNotification); override;



procedure TRasList.Notify(Ptr: Pointer; Action: TListNotification);
begin
 case Action of
   lnDeleted: Dispose(PRasEntry(Ptr));
 end;
end;


 
Anatoly Podgoretsky ©   (2009-08-07 13:31) [17]

> _Андрей  (07.08.2009 13:03:07)  [7]

Знание, что это указатель на TRasEntry и что его надо финализировать.


 
Юрий Зотов ©   (2009-08-07 13:39) [18]

> _Андрей

Кстати, в [16] дело сказано. Иначе будет неверно работать удаление из списка (Delete и Remove).


 
_Андрей   (2009-08-07 13:48) [19]


> Юрий Зотов ©   (07.08.09 13:39) [18]


Имеется ввиду Delete и Remove класса TList, да?

То есть нужно, например, добавить функцию:


> TRasList.RasDelete(Index: integer);
> begin
>    Dispose(PRasEntry(Items(Index)));
>    Delete(Index)
> end


Хотя вариант лучшего "покруче" будет...


 
Kolan ©   (2009-08-07 13:50) [20]

Я об обычных объектах, которые наследники TObject.

То есть почему бы не написать так:
TRasEntry = class
...

и не использовать TObjectList?


 
Kolan ©   (2009-08-07 13:52) [21]

Да, а не счет того, что этот набор данных не достоин стать объектом из-за того, что он слишком прост, то стоит только сделать объект, как найдутся методы, которые удобно поместить именно в нем. Про это рассказывается в книге Мартина Фаулера «Рефакторинг».


 
MsGuns ©   (2009-08-07 13:54) [22]

> Best of the best   (07.08.09 13:29) [16]

А почему бы не так:

while Count>0 do
 begin
   Dispose(PRasEntry(Items[0]);
   Delete(0);  
 end;
inherited


 
MsGuns ©   (2009-08-07 13:56) [23]

>Kolan ©   (07.08.09 13:50) [20]

Потому что для простых объектов-записей (типа сабжа) писАть классы - только загромождать код


 
Kolan ©   (2009-08-07 14:00) [24]

MsGuns, вы про такое загромождение?

TRasEntry = class
public
 Name, User, Pswd, Phone: string;
end;


 
Юрий Зотов ©   (2009-08-07 14:07) [25]

> MsGuns ©   (07.08.09 13:56) [23]

[24] загромождает код куда меньше, чем [0]. Потому что не требует ни единой строчки дополнительного кода.


 
Юрий Зотов ©   (2009-08-07 14:11) [26]

> _Андрей   (07.08.09 13:48) [19]

Не нужно ничего добавлять, только код загромождаться будет. Уберите деструктор и заместите Notify, вот и все. А еще лучше - прислушаться к советам и использовать класс вместо записи, так проще и безопаснее.


 
MsGuns ©   (2009-08-07 14:58) [27]

>Kolan ©   (07.08.09 14:00) [24]

Действительно:)
А я ж думал с пропертями вместо простых переменных, конструкторами-деструкторами :)


 
DVM ©   (2009-08-07 15:12) [28]


> TRasEntry = class
> public
>  Name, User, Pswd, Phone: string;
> end;

В таких классах нет смысла. Класс ради класса.


 
Игорь Шевченко ©   (2009-08-07 15:13) [29]

Kolan ©   (07.08.09 14:00) [24]

Ты ж Фаулера учил, который пишет, что от таких классов надо избавляться ? А сам рекомендуешь, как же так ? :)


 
Kolan ©   (2009-08-07 15:49) [30]

Игор, я помню про то, что такие классы, без инкапсуляции, лучше не делать. Но. 1. Запись не лучше.
2. Для записи, в данном случае, приходится городить огород с удалением и прочим, а с классами такой проблемы нет.

То есть я думаю, что в сабже можно сделать рефакторинг в два этапа. На первом этапе просто заменить запись на класс и избавиться от геморроя с указателями и памятью. А когда автору понадобится модифицировать класс (если понадобится), он сделает нормальную инкапсуляцию, заменив поля одноименными свойствами.

То есть с классом у него сейчас будет меньше трудностей, а в будущем больше возможностей.


 
Игорь Шевченко ©   (2009-08-07 16:30) [31]

Kolan ©   (07.08.09 15:49) [30]

RTFS: db.pas TLookupList

Не сделали классом. Ибо нефиг



Страницы: 1 вся ветка

Текущий архив: 2009.10.11;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.043 c
6-1207908296
webSQLNeederr
2008-04-11 14:04
2009.10.11
Получить в переменную Делфи значение из переменной JavaScript


1-1219124183
checkmate-maker
2008-08-19 09:36
2009.10.11
Редактор с картинками


6-1207843756
anton
2008-04-10 20:09
2009.10.11
поменять стиль документа


2-1249671660
Б
2009-08-07 23:01
2009.10.11
Положение колёсика мыши.


15-1250080125
desc
2009-08-12 16:28
2009.10.11
Принтер HP Officejet K7100 Series, печатает цветные полосы,