Форум: "Основная";
Текущий архив: 2004.03.03;
Скачать: [xml.tar.bz2];
ВнизСоздание динамической струскуры хранения данных. Найти похожие ветки
← →
Erik (2004-02-19 16:17) [0]А решил отказатся от своих любимых динамических масивов. Но Переходить на TList нерешаюсь. Пока думаю воспользоватся следующей конструкцией.
TDynamicArray = class(TObject)
private
FData: Pointer;
FCount: Integer;
procedure SetCount(c: Integer)
protected
FItemSize: Integer;
procedure GetItem(i: Integer; var Result);
procedure SetItem(i: Integer; var value);
public
constructor Create; virtual;
destructor Destroy; override;
property ItemSize: Integer read FItemSize;
property Count: Integer read FCount write SetCount; // Write this to resize array.
property Data: Pointer read FData; // Read-only pointer to data.
end;
constructor TDynamicArray.Create;
begin
inherited Create;
// You have to set FItemSize in the constructor of your descendant classes!
FItemSize := 1;
// Start your array with room for a certain number of elements by setting Count.
Count := 0;
end;
destructor TDynamicArray.Destroy;
begin
// Dispose of the array data.
FreeMem(FData, FCount * FItemSize);
inherited Destroy;
end;
procedure TDynamicArray.SetCount(c: Integer);
begin
// Resize the array by reallocating FData.
FCount := c;
ReAllocMem(FData, FCount * FItemSize);
end;
procedure TDynamicArray.GetItem(i: Integer; var Result);
var
p: Pointer;
begin
if i >= Count then raise Exception.Create("Array index out of bounds.")
else begin
p := FData;
INC(Integer(p), i * FItemSize);
CopyMemory(@Result, p, FItemSize);
end;
← →
Семен Сорокин (2004-02-19 16:23) [1]так чем TList то не нравится, там все это реализовано с гораздо-большими возможностями?
← →
Erik (2004-02-19 16:27) [2]Будет многопользовательский доступ к данным, тоесть много читателей и много писателей. Еще хочется проверять эти данные их основного модуля и если появились изменения отображать(для отладки).
Есть множество возможностей для организации, какую лучше выбрать?
P.S.
В старом варианте переодически вылетал
Exception occured at $004069B4 (Module "System", Procedure "@DynArrayClear", Unit "", Line 0)
А структура была такая
IDStat = (idGive, idSave, idCancel, idFree);
RInd = record
User: Integer;
Id: Integer;
end;
THole = Record
Params: RTKParams;
Stat: IDStat;
end;
TArrHole = Array of THole;
PArrHole = ^TArrHole;
TCounter = Record
UserID: Cardinal;
Hole: TArrHole; //array one user of reservi aeg
end;
FList: Array of TCounter; - это исходная переменая
FLock: TMultiReadExclusiveWriteSynchronizer; - это для обеспечения раздельного доступа.
← →
Amoeba (2004-02-19 16:31) [3]Изобретаем очередной велосипед?
Посмотри на http://home.earthlink.net/~akonshin/delphi_ru.htm
← →
Владислав (2004-02-19 16:32) [4]Появится новый велосипед с квадратными колесами.
← →
Тимохов (2004-02-19 16:37) [5]
> Erik © (19.02.04 16:17)
Если записи содержат динамические массивы, строки или интерфейсы, то работать с их памятью напрямую можно только при определенных знаниях.
Если уж очень хочется, то почитай про методы initialize и finalize.
Я в принципе пользуюсь паямой работой с памятью, содержащей указанные типы. Но в этом случае надо быть предельно аккуратным.
Согласен со многими тут - пользуйся TList, создавая элементы методом new и удаляя методом dispose. В этом случае дельфа сама на себя возьмет заботу о дин. массивах, строках и интерфейсах.
← →
Тимохов (2004-02-19 16:38) [6]вот такая работа
> CopyMemory(@Result, p, FItemSize);
может привести при определенных условиях фиг знает к чему.
← →
Тимохов (2004-02-19 16:42) [7]
> Будет многопользовательский доступ к данным, тоесть много
> читателей и много писателей. Еще хочется проверять эти данные
> их основного модуля и если появились изменения отображать(для
> отладки).
К выбору структуры данных это не имеет отношения - в любом случае при изменении глобальных данных вам понадобятся средства синхронизации потоков.
← →
Erik (2004-02-19 17:01) [8]Я заню про проблемы, поскольку проэкт уже реализован и кое как работает, иногда по 4 для подряд! :)
Я у себя Move и пр... использую но получилось ненадежно. :( Так вроде все отладил, но в многопользовательской среде появились проблемы. Причем несразу а через большой промежуток времени. Вот я и решил все радикально поменять. Можно и TList использовать, но все равно придется блок памяти самому распределять. Так какая разница где хранить указатели на эти блоки?
← →
Курдль (2004-02-19 17:06) [9]TList - не панацея. Всего-навсего массив указателей.
Всегда лучше опираться на конструкции более высокого уровня.
Если Вы работаете с объектами - используйте для них TCollection.
← →
Erik (2004-02-19 17:23) [10]To Курдль
Помоему я описал с чем я работаю, объектами у меня и непахнет.
Размышляю я, что и как лучше сделать. Вот и мнениями других специалистов поинтересовался.
← →
Тимохов (2004-02-19 17:26) [11]
> Можно и TList использовать, но все равно придется блок памяти
> самому распределять. Так какая разница где хранить указатели
> на эти блоки?
Почитайте кусочек 5 про new и dispose. И вообще не копируйте область памяти - делайте присвоение явно:
sourcerec.f1 := destinationrec.f1;
....
sourcerec.fn := destinationrec.fn;
Это куда надежней.
Если есть проблемы в многопользовательском режиме - надо использвать методы синхронизации - крит. секции, мьютексы или еще что.
← →
jack128 (2004-02-19 17:39) [12]
> создавая элементы методом new и удаляя методом dispose.
> В этом случае дельфа сама на себя возьмет заботу о дин.
> массивах, строках и интерфейсах.
Это точно?
То есть такой код
var
p: PString;
begin
New(p);
p^ := StringOfChar("q", 10);
Dispose(p);
end; не приведет к утечке памяти??
← →
Erik (2004-02-19 17:42) [13]To Тимохов
Количество параметров по пременое, занчить и разное количество памяти выделять надо. Зачить одним New необойдешся. Придется еще кусочик прикрепить.
← →
Vuk (2004-02-19 17:43) [14]У меня есть реализация похожей структуры данных. В реализации разница в том, что при помещении данных в контейнер происходит копирование данных, а при получении возвращается указатель на хранящиеся в контейнере данные.
MaxArraySize = $1FFFFFFF;
PByteArray = ^TByteArray;
TByteArray = array [ 0..MaxArraySize ] of byte;
function TCLArray.Get( Index : integer ) : Pointer;
begin
{$ifdef ThreadSafe}
Lock;
{$endif}
CheckIndex( Index );
Result := @PByteArray( FItems )^[ Index * FItemSize ];
end;
procedure TCLArray.Put( Index : integer; Item : pointer );
begin
{$ifdef ThreadSafe}
Lock;
{$endif}
System.Move( Item^, PByteArray( FItems )^[ Index * FItemSize ], FItemSize );
end;
← →
Игорь Шевченко (2004-02-19 17:46) [15]
> проэкт уже реализован и кое как работает, иногда по 4 для
> подряд! :)
Замечательный критерий :))
← →
Владислав (2004-02-19 17:52) [16]> jack128 © (19.02.04 17:39) [12]
А что? Должен?
← →
Курдль (2004-02-19 18:00) [17]
> To Курдль
> Помоему я описал с чем я работаю, объектами у меня и непахнет.
>
> Размышляю я, что и как лучше сделать. Вот и мнениями других
> специалистов поинтересовался.
Я, конечно, не специалист, но мне сдается, что Вы - засланный казачок из сей каких-нить. :)
Вы полностью подменяете обязанности компиллятора высокого уровня по заботе о выделении, размещении, освобождении памяти и т.п.
Если хотите, чтобы все супер-быстро работало - вернитесь за си и вперед! А если пишете на ЭТОМ - пользуйтесь ЕГО прелестями.
По существу. Вы предположили поработать с TList.
Я рекомендую TCollection. По сути своей это ничего не меняет - какие-то указатели, указатели на функции, указатели на указатели и т.п. Однако в design-time это даст огромные преимущетсва. Например, ссылки по индексам не только к объектам, но и к их свойствам; автоматическое добавление объектов вместе со всеми их причиндалами по Add, освобождение памяти по Free и т.п. Разве это плохо?
← →
Юрий Зотов (2004-02-19 18:01) [18]> Erik © (19.02.04 16:27) [2]
> Будет многопользовательский доступ к данным, тоесть много
> читателей и много писателей
TThreadList. И все. Уже готовое. Без велосипедов.
← →
Тимохов (2004-02-19 18:04) [19]
> jack128 © (19.02.04 17:39) [12]
>
> var
> p: PString;
> begin
> New(p);
> p^ := StringOfChar("q", 10);
> Dispose(p);
> end; не приведет к утечке памяти??
Утечки не будет, т.к. pstring типизированный указатель.
← →
Vuk (2004-02-19 18:08) [20]to Юрий Зотов:
TThreadList, оно, конечно, хорошо, но там защита экземпляра целиком. Иногда лучше иметь защиту на уровне методов. К тому же, насколько я понимаю, стоит задача работать с данными любого размера, а не только с указателями. Так что велосипед иной раз может и пригодиться.
← →
VLAD-MAL (2004-02-19 18:10) [21]Фундаментальные алгоритмы и структуры данных в Delphi
The Tomes of Delphi Algorithms and Data Structures
Джулиан Бакнелл
Цена: 278 руб.
На складе.
Вес: 510 грамм
Мягкая обложка, 560 стр., 2003 г.
Издательство: ДиаСофтЮП
ISBN 5-93772-087-3, 1-55622-736-1, Тираж: 3000 экз., Формат: 70x100/16
Афигенная книга!
← →
jack128 (2004-02-19 18:13) [22]
> Владислав © (19.02.04 17:52) [16]
> > jack128 © (19.02.04 17:39) [12]
>
> А что? Должен?
Это зависит от того файнализирует ли Dispose строку. Вот
> Тимохов © говорит, что все ок.
> Тимохов © (19.02.04 18:04) [19]
А можно ссылку на хелп где это написано? ;-) Или ты это в asm коде высмотрел??
← →
Тимохов (2004-02-19 18:16) [23]
> jack128 © (19.02.04 18:13) [22]
Глянь finalize+f1
← →
Юрий Зотов (2004-02-19 18:16) [24]> Vuk © (19.02.04 18:08) [20]
Разве защита внутри Add, Insert, Remove и пр. - это не защита на уровне методов?
Разве работа с указателями - это не работа с данными любого размера?
То, что велосипед иной раз может и пригодиться... ну, как раз мы-то это прекрасно знаем - поскольку как раз велосипеды в основном и изобретаем, работа такая. Но ведь никто из нас не станет изобретать стандартный велосипед?
← →
Тимохов (2004-02-19 18:23) [25]
> Тимохов © (19.02.04 18:16) [23]
Я тут посмотрел сам куда Вас послал и был удивлен следующей фразой оттуда
Dynamic arrays can never be deallocated using the Dispose procedure, but can be freed by passing them to Finalize.
Не полнял я ее, что-то. В реальности dipose корректно освобождает и дин. массивы. О чем тут речь? Может вы лучше английский знаете?
← →
Тимохов (2004-02-19 18:24) [26]
> Юрий Зотов © (19.02.04 18:16) [24]
Вы, конечно, знаете, но все же замечу TThreadList не позволяет одновременное чтение.
← →
Vuk (2004-02-19 18:24) [27]to Юрий Зотов:
>Разве защита внутри Add, Insert, Remove и пр. - это не защита на
>уровне методов?
"Недобросовестный" код может легко развалить защиту TThreadList. Достаточно вызвать LockList, запомнить ссылку, потом UnlockList. Все, с полученной ссылкой можно делать все что душе угодно невзирая на то, что происходит в других потоках.
>Разве работа с указателями - это не работа с данными любого
>размера?
4 байта. :o) А если мне нужен, например, список указателей на методы? Мне выделять вагон блоков по 8 байт в куче или лучше все-таки хранить их в некоем подобии динамического массива?
← →
Владислав (2004-02-19 18:24) [28]> jack128 © (19.02.04 18:13) [22] & Тимохов © (19.02.04 18:16) [23]
Есть еще один способ :)
procedure TForm1.Button1Click(Sender: TObject);
var
p: PString;
i: Integer;
begin
for i := 1 to 100000 do
begin
New(p);
p^ := StringOfChar("q", 100000);
Dispose(p);
end
end;
← →
Тимохов (2004-02-19 18:26) [29]
> Владислав © (19.02.04 18:24) [28]
> > jack128 © (19.02.04 18:13) [22] & Тимохов ©
Способ чего?
Каков Ваш тезис?
← →
Romkin (2004-02-19 18:27) [30]Есть велосипед покруче, TMultiReadExclusiveWriteSynchronizer
одно название чего стоит :)))
Это для тех, кто любит одновременное чтение :)
← →
Serginio666 (2004-02-19 18:32) [31]2 VLAD-MAL (19.02.04 18:10) [21]
Книга расчудеснейшая, только тираж маловат.
← →
Владислав (2004-02-19 18:32) [32]> Vuk © (19.02.04 18:24) [27]
"4 байта. :o) А если мне нужен..."
Это точно...
А вообще-то, я в [4] имел ввиду несколько иное.
Написать "свой" TList или TThreadList под конкретный тип данных, чтобы оптимизировать обращение к ним... Полностью согласен, если овчинка выделки стоит... А вот писать такое обобщенное хранилище, ИМХО, только для общего развития. Где еще можно так "наколоться" или перечитать столько различной документации, чтобы хоть что то заработало?
← →
Владислав (2004-02-19 18:35) [33]> Тимохов © (19.02.04 18:26) [29]
На моей конфигурации просто нет столько памяти, чтобы работа этой процедуры завершилась корректно ;)
> Romkin © (19.02.04 18:27) [30]
Ну дык у этого "велосипеда" есть более короткое и более непонятное "название" ;)
← →
Vuk (2004-02-19 18:36) [34]to Владислав:
>А вот писать такое обобщенное хранилище, ИМХО, только для общего
>развития.
Ну не знаю. В работе очень даже помогает. Пример со списком методов - из реальной жизни.
← →
Владислав (2004-02-19 18:41) [35]> Vuk © (19.02.04 18:36) [34]
Как не крути, с типизированными "штуками" позже работать проще, а то, что помогает, ни сколько не спорю.
← →
Vuk (2004-02-19 18:43) [36]to Владислав:
>с типизированными "штуками" позже работать проще
Кто бы спорил, только вот динамические массивы для этого не совсем удобны.
← →
Владислав (2004-02-19 18:48) [37]> Vuk © (19.02.04 18:43) [36]
Почти сдаюсь :) В точку бьете ;) Хотя, в принципе, в [32] я об этом если не сказал, то обмолвился точно :)
← →
Serginio666 (2004-02-19 18:58) [38]2 Vuk ©
Реализация типизированного TList (StringList) на динамическом массиве это пара минут. Все что нужно это Count (капасити это Length), SetLength да правильно использовать Move , не забывая обниливать без вызова финализатора объеты поддерживающих подсчет ссылок.
← →
Тимохов (2004-02-19 18:58) [39]
> Владислав © (19.02.04 18:35) [33]
> > Тимохов © (19.02.04 18:26) [29]
>
> На моей конфигурации просто нет столько памяти, чтобы работа
> этой процедуры завершилась корректно ;)
Вы ошибаетесь - код выполнятется с нулевой утечкой памяти. Проверьте.
← →
Vuk (2004-02-19 19:01) [40]Для примера. Вот код, который реализует список методов с минимальным сервисом (добавление/удаление/доступ к элементам) на основе того класса, фрагменты которого я приводил. Все вполне типизировано.
type
TMethodList = class(TObject)
private
FMethods: TCLArray;
function GetMethods(index: integer): TMethod;
procedure SetMethods(Index: integer; Value: TMethod);
function GetCount: integer;
public
constructor Create;
procedure Add(Handler : TMethod);
procedure Remove(Handler : TMethod);
procedure Delete(Index: integer);
property Count: integer read GetCount;
property Methods[index:integer]: TMethod read GetMethods write SetMethods;
end;
constructor TMethodList.Create;
begin
inherited Create;
FMethods := TCLArray.Create(10, 10, sizeof(TMethod));
end;
procedure TMethodList.Add(Handler : TMethod);
begin
FMethods.Add(@Handler);
end;
function TMethodList.GetMethods(index: integer): TMethod;
begin
Result := TMethod(FMethods.Items[index]^);
end;
procedure TMethodList.Remove(Handler : TMethod);
var
i : integer;
begin
i := FMethods.IndexOf(@TMethod(Handler));
if i <> -1 then
FMethods.Delete(i);
end;
procedure TMethodList.Delete(Index: integer);
begin
FMethods.Delete(Index);
end;
procedure TMethodList.SetMethods(Index: integer; Value: TMethod);
begin
FMethods.Items[index] := @Value;
end;
function TMethodList.GetCount: integer;
begin
Result := FMethods.Count;
end;
Для примера можете подумать, что нужно для реализации того же самого на динамических массивах. Особенно это касается удаления. :o)
← →
Владислав (2004-02-19 19:08) [41]> Тимохов © (19.02.04 18:58) [39]
Возможно я некорректно выразился, но именно это и имел ввиду. Утечки памяти нет. Фраза означала, что проверить, есть утечка или нет, поможет подобный код.
> Vuk © (19.02.04 19:01) [40]
Оценил бы, да понятия не имею, что такое TCLArray. Просветите плиз.
← →
romeo (2004-02-19 19:12) [42]
> Тимохов © (19.02.04 17:26) [11]
CopyMemory плохо только в этом случае, или лучше вообще этим не пользоваться? Например, для копирования значений из одного массива в другой, типа:
var
DinArr: array of integer;
const
Arr: array [0..50] of integer = (1, ...);
SetLength(DinArr, Length(Arr))
CopyMemory(DinArr, @Arr, Length(Arr)*SizeOf(Integer))
Или тоже лучше делать почленное копирование?
← →
Владислав (2004-02-19 19:15) [43]В данном случае ничего плохого не произойдет. Правда всегда есть оговорка ;) Вот она: Если, например, к типу Integer не преобразованы указатели на переменные типа string ;)
← →
Vuk (2004-02-19 19:16) [44]to Владислав:
>Просветите плиз.
Привожу только интерфейс. Полный код - это слишком много. Писалось сие давно, поэтому по интерфейсу и реализации это что-то среднее между TList и коллекциями из Turbo Pascal (касается управления памятью).
TCLArray = class( TCLContainer )
protected
FCount : integer;
FLimit : integer;
FDelta : Integer;
FItemSize : integer; { размер одного элемента }
FItems : Pointer; { указатель на непрерывный блок памяти, содержащий элементы массива }
function Get( Index : integer ) : Pointer; override;
procedure Put( Index : integer; Item : Pointer ); override;
function GetCount: integer; override;
procedure AssignTo( Dest : TPersistent); override;
procedure AppendItem( Item : pointer; AssignMode : boolean ); override;
procedure SetLimit( ALimit : integer ); virtual;
procedure CheckIndex( Index : integer );
procedure SetCount( NewCount : integer );
procedure IndexError;
public
constructor Create( ALimit, ADelta, AItemSize : Integer );
destructor Destroy; override;
procedure Add( Item : pointer ); override;
procedure Clear; override; {обнуление всех элементов}
procedure Swap( Index1, Index2 : integer ); override;
procedure Insert( Index : integer; Item : pointer );
function IndexOf(Item: pointer): integer; override;
procedure Delete( Index : integer ); override;
procedure DeleteAll; override; {удаление всех элементов}
property Items;
property Count : integer read FCount write SetCount;
property Limit : integer read FLimit write SetLimit;
property Delta : integer read FDelta write FDelta;
property ItemSize : integer read FItemSize;
property Data : pointer read FItems;
end;
← →
Тимохов (2004-02-19 19:18) [45]
> Владислав © (19.02.04 19:15) [43]
> Если, например, к типу Integer не преобразованы указатели
> на переменные типа string ;)
Глубоко копаете. Но в случае если это указатели, преобразованные к integer, никакого подсчета ссылок и для почленного копирование не будет.
> romeo © (19.02.04 19:12) [42]
Для массивов integer все будет ок.
← →
Владислав (2004-02-19 19:28) [46]> Тимохов © (19.02.04 19:18) [45]
Смайлик, однако...
Подсчета ссылок не будет. Будет, как пить дать, обращение по битым ссылкам (это относится только к вашему утверждению. Типа, почти оффтоп).
> Vuk © (19.02.04 19:16) [44]
Ну судя по приведенному, именно не типизированное, приведенное в типизированное :) Типа, одобрям :) Надо взять на заметку.
А вот это ADelta (если я правильно понял) просто здорово. Чего, боюсь, автору вопроса очень не хватает. В общем, очень интересно!
← →
Vuk (2004-02-19 19:34) [47]>А вот это ADelta (если я правильно понял) просто здорово.
Delta - это значение (в элементах массива), на которое увеличивается размер массива при вызовах Add и Insert, если количество элементов уже достигло максимально возможного на данный момент (limit). В TList происходит то же самое, но регулировать величину приращения нельзя - она вычисляется автоматически. В принципе, обе стратегии выделения памяти имеют право на существование. Если бы я писал сейчас, то у меня Delta задавалась бы в процентах от текущего размера.
← →
Владислав (2004-02-19 19:48) [48]> Vuk © (19.02.04 19:34) [47]
"...задавалась бы в процентах от текущего размера."
Ну да, конечно, но зависит, таки от конкретной задачи. Я у себя отказался от ТАКОГО "расширения". Проценты "увеличивают" размер занимаемой памяти каждый раз на все большую величину. Я в своей задаче не знал, как он будет увеличиваться. Остановился на таком варианте. Сначала проценты (до некоторого размера), потом жестко заданный прирост.
Насчет дельты, надеюсь, Вы меня поняли :)
← →
Vuk (2004-02-19 20:10) [49]to Владислав:
>Насчет дельты, надеюсь, Вы меня поняли
Ну... В принципе, для таких вещей можно сделать что-то типа отдельных классов, которые реализуют разные стратегии управления памятью, а класс-массив уже использует какой-то из таких классов для своей работы. Тогда можно будет реализовать сколько угодно таких стратегий и даже менять их налету. :o)
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2004.03.03;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.007 c