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

Вниз

Самоосвобождающиеся данные   Найти похожие ветки 

 
Лёша   (2009-06-26 15:12) [0]

Вопрос не по конкретной задаче, а теоритический.
Имеется объект

TDefault = class
 Data: Pointer;
end;

В Data я хочу хранить разношерстные записи, например:

TAnyRec = record
Fild1, Fild2: integer;
end;

Или, для другого случая - другой рекорд.

Для этого я выделяю память под запись от
PAnyRec = ^TAnyRec;

А когда ненужна - освобождаю.

Теперь вопрос, что можно придумать, чтобы память освобождалась автоматически по уничтожению TDefault? Не меняя сам класс TDefault.

Хочу проверить правильность след. утверждений.

1. Для каждого типа хранящихся данных делаем обертку в виде интерфейса. Класс, реализующий интерфейс имеет поле типа TAnyRec. Выделять память под TAnyRec не надо. Она сама очистится, когда самоуничтожится интерфейс. Интерфейс сохраняем так:

WrapInterface: TWrapInterface;
...
Default.Data := Pointer(WrapInterface);

2. Для каждого типа хранящихся данных делаем обертку в виде дин. массива.  TAnyArray = array or TAnyRec;
Данные сохраняем так:

Setlength(TAnyArray(Default.Data), 1);
TAnyArray(Default.Data)[0] := ... ;//заполняем данные

И есть еще варианы сабжа?


 
Сергей М. ©   (2009-06-26 15:19) [1]


> есть еще варианы сабжа?


Data: [Ole]Variant;
Data: IUnknown;


 
int64   (2009-06-26 15:23) [2]

Это понятно.
Но у меня вопрос только для случая Data: Pointer;


 
DVM ©   (2009-06-26 15:26) [3]


> чтобы память освобождалась автоматически по уничтожению
> TDefault? Не меняя сам класс TDefault.

Ну если в рекордах в свою очередь не будет строк и прочих сложных объектов, то достаточно в классе хранить еще размер рекорда, тогда просто FreeMem можно сделать будет.


 
int64   (2009-06-26 15:28) [4]

DVM ©   (26.06.09 15:26) [3]

> Не меняя сам класс TDefault.


Лёша


 
RWolf ©   (2009-06-26 15:30) [5]

Вариант — сделать TDefault абстрактным базовым, а все варианты хранения данных описать в его наследниках.


 
int64   (2009-06-26 15:44) [6]

RWolf ©   (26.06.09 15:30) [5]
Вы опять подходите к решению задачи. Её можно решить множеством способов.

> Вопрос не по конкретной задаче, а теоритический.

Есть указатель. По нему хранятся разные данные. Как можно хранить эти данные, не заморачиваясь об их освобождении?

Леша.


 
int64   (2009-06-26 15:54) [7]

Собственно, двух вариантов хватит.
Но такое подозрение. Подсчет ссылок для 1 и 2 будет вестись корректно? Если нет, возможно, сабж не разрешим.

Лёша.


 
Сергей М. ©   (2009-06-26 15:58) [8]


> Есть указатель. По нему хранятся разные данные. Как можно
> хранить эти данные, не заморачиваясь об их освобождении?
>


Никак.
Нетипизированный указатель не относится к типу данных с управляемым временем жизни.


 
RWolf ©   (2009-06-26 15:58) [9]


> Есть указатель. По нему хранятся разные данные. Как можно
> хранить эти данные, не заморачиваясь об их освобождении

Хранить в этом указателе адрес объекта, содержащего нужные данные — он сам знает, как освобождать свою память.


 
RWolf ©   (2009-06-26 15:58) [10]

Хранить в этом указателе адрес объекта, содержащего нужные данные — он сам знает, как освобождать свою память.


 
int64   (2009-06-26 16:05) [11]

Сергей М. ©   (26.06.09 15:58) [8]

Т.е. в приведенных мною 2-х вариантах будут утечки памяти.

Лёша.


 
Сергей М. ©   (2009-06-26 16:08) [12]

Будут, если ты не предпримешь явные телодвижения по освобождению.
А если предпримешь, то это противоречит постановке задачи - "самоосвобождение".


 
DVM ©   (2009-06-26 16:16) [13]


> Лёша

Если класс не допускает модификации, то записи то модификацию допускают? Если да, то пусть первым полем записи будет число, означающее ее размер, тогда в деструкторе класса можно будет освободить область памяти, занимаемую записью.


 
int64   (2009-06-26 16:17) [14]

С интерфейсами понятно. Возможно, когда мы копируем указатель, а не ссылку, подсчет ссылок не ведется. Это я могу проверить.
А как проверить утечки при динамичиских массивах.
Почему, например, при вызовах
Setlength(TAnyArray(Default.Data), 1) будут утечки?

Лёша.


 
int64   (2009-06-26 16:24) [15]

RWolf ©   (26.06.09 15:58) [10]
Это означает, что вместо явного управления памяти, мы, всего лишь, явно рулим временем жизни какого-то объекта.
Шило на мыло.

Лёша


 
int64   (2009-06-26 16:31) [16]

DVM ©   (26.06.09 16:16) [13]

> Если класс не допускает модификации, то записи то модификацию
> допускают? Если да, то пусть первым полем записи будет число,
>  означающее ее размер, тогда в деструкторе класса можно
> будет освободить область памяти, занимаемую записью.

Опять же, логику освождения данных выносим в сам TDefault (который у меня ничего об этом знать не должен).
Это то же, что мы руками выделяем-освобождаем память внешними методами.

Лёша.


 
Сергей М. ©   (2009-06-26 16:35) [17]


> Почему, например, при вызовах
> Setlength(TAnyArray(Default.Data), 1) будут утечки?


Потому что чтобы их не было, при разрушении объекта TDefault надо явно вызвать, скажем, Setlength(TAnyArray(Default.Data), 0).
А это противоречит условию "самоосвобождения".


 
Sapersky   (2009-06-26 16:50) [18]

Для структурных типов, содержащих управляемые указатели (длинные строки, дин. массивы) можно сделать корректное освобождение, используя TypeInfo - некое подобие "внутренней RTTI". Но ТОЛЬКО для управляемых, неуправляемые (классы, прочие указатели) в TypeInfo не входят.
Вот, например, модификация TList c автоматическим прибиванием записей:


 TRecordList = class(TList)
 private
   FRecSize: Integer;
   FTypeInfo : Pointer;
 protected
   function AllocNew(Src: Pointer): Pointer;
   procedure Notify(Ptr: Pointer; Action: TListNotification); override;
 public
   constructor Create(RecSize: Integer; TypeInfo : Pointer);
   function Add(Src: Pointer): Integer;
   procedure Insert(Index: Integer; Src: Pointer);
 end;

{ TRecordList }

constructor TRecordList.Create(RecSize: Integer; TypeInfo: Pointer);
begin
FRecSize := RecSize; FTypeInfo := TypeInfo;
end;

procedure xInitialize(p: Pointer; typeInfo: Pointer; elemCount: Cardinal);
asm
CALL    System.@InitializeArray;
end;

procedure xFinalize( P: Pointer; Info: Pointer; elemCount: integer );
asm
CALL    System.@FinalizeArray;
end;

procedure xCopy( Dst, Src : Pointer; Info: Pointer; Cnt: Integer);
asm
 push Cnt
 CALL    System.@CopyArray;
//  add esp, 4 // for some reason we don"t need stack restoring
end;

function TRecordList.AllocNew(Src: Pointer): Pointer;
begin
If (FRecSize <> 0) then begin
 GetMem(Result, FRecSize);
 If (FTypeInfo <> nil) then xInitialize(Result, FTypeInfo, 1);
 If (Src <> nil) then
   If (FTypeInfo <> nil) then xCopy(Result, Src, FTypeInfo, 1)
                         else System.Move(Src^, Result^, FRecSize);
end else
 Result := Src;
end;

function TRecordList.Add(Src: Pointer): Integer;
begin
Result := Inherited Add( AllocNew(Src) );
end;

procedure TRecordList.Insert(Index: Integer; Src: Pointer);
begin
Inherited Insert(Index, AllocNew(Src) );
end;

procedure TRecordList.Notify(Ptr: Pointer; Action: TListNotification);
begin
If (FRecSize <> 0) and (Action = lnDeleted) then begin
 If (FTypeInfo <> nil) then xFinalize(Ptr, FTypeInfo, 1);
 FreeMem(Ptr);
end;
Inherited Notify(Ptr, Action);
end;


Использование - для записей, не содержащих строк и пр. managed типов:
List := TRecordList.Create(SizeOf(TMyRecord), nil);
для содержащих:
List := TRecordList.Create(SizeOf(TMyRecord), TypeInfo(TMyRecord));

Ещё по теме манипуляций с TypeInfo:
http://sapersky.narod.ru/files/TypInfoEx.rar



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

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

Наверх




Память: 0.52 MB
Время: 0.012 c
15-1245082595
DillerXX
2009-06-15 20:16
2009.08.30
Советский усилитель


15-1246307568
Юрий
2009-06-30 00:32
2009.08.30
С днем рождения ! 30 июня 2009 вторник


2-1246696231
Gray
2009-07-04 12:30
2009.08.30
Ккак узнать номер выделенной строки в DBGrid?


15-1245481247
Gydvin
2009-06-20 11:00
2009.08.30
Детские сиденья


1-1213595605
ganda
2008-06-16 09:53
2009.08.30
DevEpress Grid + фильтрация