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

Вниз

От теории к практике   Найти похожие ветки 

 
ProgRAMmer Dimonych ©   (2007-12-04 00:46) [0]

В очередной раз сталкиваюсь с такой проблемой: в теории вроде всё понятно, апредсказать поведение программы на практике боюсь :)

Короче говоря, о том, что объект в Delphi есть не что иное как указатель в размере 4 байт (для 32-битных ОС) на кусок памяти, соответствующий этому самому объекту, я знаю и с этим вполне смирился :), хотя и смиряться-то было не с чем. А теперь собственно преамбула...

Пишу некий объект TVarList=class(TVicObject). TVarList должен быть небольшой поделкой-заменой для TList"а, Items"ы которого - не просто TObject, а TVar (тоже =class(TVicObject)), ибо умаялся приведение типов делать. Работа объекта основана на динамическом массиве указателей на TVar"ы.

Пишу метод для вставки нового TVar"а на заданную позицию Index.

procedure TVarList.Insert(Index:Integer);
var
 i,L:Integer;
begin
 if (Index<0) or (Index>=Length(FList)) then Exit;
 L:=Length(FList); SetLength(FList,L+1);
 for i:=L downto Index+1 do FList[i]:=FList[i-1];
 FList[Index]:=TVar.Create(Self);
end;


При этом FList:array of TVar.

Можно ли делать именно так (особенно волнует присвоение после цикла)?


 
Германн ©   (2007-12-04 01:16) [1]


> Можно ли делать именно так (особенно волнует присвоение
> после цикла)?
>

Вроде можно. Внешне препятствий не нашел, а проверять лень.
Тем более, что это и не моя задача. :)


 
boa_kaa ©   (2007-12-04 01:20) [2]


> Можно ли делать именно так

Можно. Но лучше воспользоваться готовым TList. Что-нить вроде

TMyList = class
private
 FList:TList;
public
 procedure Insert(index: Integer; item: TMyItemClass);
end;
.....
procedure TMyList.Insert(index: Integer; item: TMyItemClass);
begin
 FList.Insert(index, item);
end;

Ну или примерно так.
Да, кстати, в TList содержатся лишь Pointer.


 
Юрий Зотов ©   (2007-12-04 01:20) [3]

Нормально, только вместо Exit стоит сделать raise. Выход за границы - это логическая ошибка в вызывающем коде, а ошибки нужно не маскировать, а наоборот, выпячивать. Иначе замучаешься их ловить, замаскированные-то.

И еще: неэффективно это - по одному кусочку память выделять. Посмотрите код TList - там хитрее сделано.


 
Leonid Troyanovsky ©   (2007-12-04 01:22) [4]


> ProgRAMmer Dimonych ©   (04.12.07 00:46)  

> Пишу некий объект TVarList=class(TVicObject). TVarList должен
> быть небольшой поделкой-заменой для TList"а, Items"ы которого
> - не просто TObject, а TVar (тоже =class(TVicObject)), ибо
> умаялся приведение типов делать.


 TMyObjectList = class(TObjectList)
   public
    function GetMyObject(Index: Integer): TMyObject;
    procedure SetMyObject(Index: Integer; AObject: TMyObject);
    property MyObjects[Index: Integer]: TMyObject read GetMyObject write SetMyObject;
 end;

procedure TMyObjectList.SetMyObject(Index: Integer; AObject: TMyObject);
begin
 // Assert((Index >= 0) and (Index < Count));
 Items[Index] := AObject;
end;

function TMyObjectList.GetMyObject(Index: Longint): TMyObject;
begin
 // Assert((Index >= 0) and (Index < Count));
 Result := TMyObject(Items[Index]);
end;


--
Regards, LVT.


 
palva ©   (2007-12-04 09:52) [5]

ProgRAMmer Dimonych ©   (04.12.07 00:46) [0]

> for i:=L downto

По-моему, здесь ошибка. Надо:
for i:=L+1 downto


 
Думкин ©   (2007-12-04 10:00) [6]


> palva ©   (04.12.07 09:52) [5]


> do FList[i]:=FList[i-1];


 
palva ©   (2007-12-04 10:44) [7]

Наверно, я чего-то не понимаю. L - это старая длина списка. Добавился новый элемент с индексом L+1, и он должен быть заполнен при сдвиге элементов. Поэтому цикл надо начинать с i=L+1 ?


 
Думкин ©   (2007-12-04 10:48) [8]


> palva ©   (04.12.07 10:44) [7]

Длина L+1, индекс же последнего L. Мы с нуля начинаем или где?


 
Skyle ©   (2007-12-04 10:51) [9]


> palva ©   (04.12.07 10:44) [7]
> Наверно, я чего-то не понимаю. L - это старая длина списка.
>  Добавился новый элемент с индексом L+1

Добавился новый элемент с индексом L, который по счёту L+1"й.


>  L:=Length(FList);
>  SetLength(FList,L+1);


 
palva ©   (2007-12-04 11:18) [10]

Думкин ©   (04.12.07 10:48) [8]
Skyle ©   (04.12.07 10:51) [9]
Ага, теперь понял свою ошибку.
Привык к бейсиковскому ReDim, где надо указывать новый верхний индекс.


 
Skyle ©   (2007-12-04 11:23) [11]


> palva ©   (04.12.07 11:18) [10]
> Привык к бейсиковскому ReDim, где надо указывать новый верхний
> индекс.

Я почему-то именно так и подумал :)


 
ProgRAMmer Dimonych ©   (2007-12-04 15:53) [12]

Всем спасибо за ответы.

В двух словах отвечу на основные возникшие вопросы...

> boa_kaa ©   (04.12.07 01:20) [2]

TList не пользую, ибо Classes, а мне на данный момент только Windows да Messages полагается. :) Не предупредил об этом, т.к. посчитал, что это не столь важно.

> Юрий Зотов ©   (04.12.07 01:20) [3]
> Нормально, только вместо Exit стоит сделать raise.

Думаю, своей предыдущей фразой я ответил на вопрос о том, почему не сделан raise. Хотя, возможно... ну, ладно...

> Выход за границы - это логическая ошибка в вызывающем коде,
>  а ошибки нужно не маскировать, а наоборот, выпячивать.
> Иначе замучаешься их ловить, замаскированные-то.

Знаю, сталкивался, даже копчик по этому поводу болел :)

> И еще: неэффективно это - по одному кусочку память выделять.
>  Посмотрите код TList - там хитрее сделано.

1. Можно на "ты".
2. Смотрел. Нет, вру... Заглядывал... Посмотрел ещё раз. Понял две вещи.

2.1. Выделение памяти производится не по одному элементу за заход, а по несколько, причём в зависимости от его (массива и, соответственно, списка) текущих размеров.
2.2. В классе объявлен указатель на массив указателей. Насколько я могу судить, это позволяет более гибко управлять размером потребляемой приложением памяти (в т.ч. и за счёт 2.1)? Отсюда же, полагаю, и ограничение на количество элементов в TStringList"е, например?

Я прав?

> Leonid Troyanovsky ©   (04.12.07 01:22) [4]

Идею, кажется, понял. Т.е. пишем class(TObjectList), который, в свою очередь, является class(TList). В своём классе прописываем свойство MyObjects, которое будет "возвращать" (хотя "возвращать", наверное, не совсем по сути... Зато более русче :)) объект нужного типа. Спасибо, буду иметь в виду. Но ввиду вышеназванного обстоятельства этот способ для этой конкретной программы мне не подходит.

С
> palva ©   (04.12.07 09:52) [5]
по
> Skyle ©   (04.12.07 11:23) [11]

А я-то в своё время намучился. :) Ну, в Pascal"е и Delphi проблема с нумерацией возникает только у динамических массивов (причём об этом сказано в соответствующем разделе справки по Delphi, поэтому, когда начинал, проблем не возникло), а статику можно и с 1 объявить :) А потом в PHP со строками поратботть решил. Чуть было не умаялся, вовремя умные люди напомнили, что там нумерация символов с нуля :)


 
ProgRAMmer Dimonych ©   (2007-12-04 15:54) [13]

P.S. Т.е., я так понимаю, мой вариант имеет право на существование, особенно после внесения поправок ( :):):) ) в выделение памяти?


 
TUser ©   (2007-12-05 11:23) [14]

Вот, чем пользуюсь я

type
 {
   Предковый класс, содержащий массив итемов такого же типа
   Функция SmartGetLength возвращает ожидаемыую длину массива, например
   для ALA - наверное, 5 и т.д. Это позволяет избежать фрагментации
   памяти. После заполнения массива лишние части удаляются процедурой
   CutArrays.
 }
 TContainer = class (* TPersistent *)
  private
   FCount: integer;
   FItems: array of TContainer;

   procedure Grow;
   function rItem (Index: integer): TContainer;
   procedure SaveToFileInternal (List: TStrings);
  protected
//    procedure CheckType (Value: TContainer); virtual; abstract;
   function SmartGetLength: integer; virtual; abstract;
   procedure StartSaveToFile (List: TStrings); virtual;
   procedure EndSaveToFile (List: TStrings); virtual;
  public
   constructor Create; virtual;
   destructor Destroy; override;
   procedure AddItem (Value: TContainer);
   procedure CutArrays;
   function GetCount: integer;
   procedure SaveToFile (FileName: string); virtual;

//    property Count: integer read FCount;
   property Items[Index: integer]: TContainer read rItem;
  end;

implementation
uses SysUtils, uPDBErrors, uPDBFast;

{  TContainer  }

procedure TContainer.Grow;
var L: integer;
begin
 inc (FCount);
 if FCount <= length (FItems) then
   Exit;

 L:=SmartGetLength;
{
 while L <= length (FItems) do
   L := L * 2;    
}
 SetLength (FItems, L);
end;

function TContainer.rItem (Index: integer): TContainer;
begin
 if (Index < 0) or (Index >= FCount) then
   raise EPdbException.Create ("Index " + inttostr(Index) +
         " is out of bounds in " + ClassName);

 result:=FItems[Index];
end;

procedure TContainer.SaveToFileInternal (List: TStrings);
var i: integer;
begin
 StartSaveToFile (List);

 for i:=0 to FCount-1 do
   FItems[i].SaveToFileInternal (List);

 EndSaveToFile (List);
end;

procedure TContainer.StartSaveToFile (List: TStrings);
begin
 // do nothing
end;

procedure TContainer.EndSaveToFile (List: TStrings);
begin
 // do nothing
end;

constructor TContainer.Create;
begin
 SetLength (FItems, 0);
 FCount:=0;
end;

destructor TContainer.Destroy;
var i: integer;
begin
 for i:=0 to length (FItems) - 1 do
  try
   FItems[i].Free;
  except
  end;
 SetLength (FItems, 0);
end;

procedure TContainer.AddItem (Value: TContainer);
var a: integer;
begin
 a:=FCount;
 Grow;
 FItems[a]:=Value;
end;

procedure TContainer.CutArrays;
var i: integer;
begin
 SetLength (FItems, FCount);
 for i := 0 to FCount - 1 do
   FItems[i].CutArrays;
end;

function TContainer.GetCount: integer;
begin
 result:=FCount;
end;

procedure TContainer.SaveToFile (FileName: string);
var List: TStrings;
begin
 List:=TStringList.Create;
 try
  SaveToFileInternal (List);
  List.SaveToFile(FileName);
 finally
  List.Free;
 end;
end;

// =======================

{
 Описывает рост динамических массивов, см. методы SmartGetLength
}
function IncInteger (const I: integer; const Ts: array of integer;
                    const LastIncrement: integer): integer;
var j: integer;
begin
 for j:=0 to high (Ts) do
   if I < Ts[j] then begin
     result:=Ts[j];
     Exit;
     end;
 result := I + LastIncrement;
end;

// =======================
// И дальше ваяю наследников типа

 TProtein = class (TContainer)
  private
   function rModels (Index: integer): TModel;
  protected
   function SmartGetLength: integer; override;
  public
   property ModCount: integer read GetCount;
   property Models[Index: integer]: TModel read rModels;
  end;

function TProtein.SmartGetLength: integer;
begin
 result:=IncInteger (GetCount, [1, 5, 10, 20, 60], 60);
end;

function TProtein.rModels (Index: integer): TModel;
begin
 result:=TModel(Items[Index]);
end;



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

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

Наверх




Память: 0.53 MB
Время: 0.018 c
2-1196845110
dmdel
2007-12-05 11:58
2007.12.30
Все те же просмотры


2-1196128643
Abcdef123
2007-11-27 04:57
2007.12.30
2 вопроса по одной теме - печать файла в отчете.


4-1181545591
AndreyRus
2007-06-11 11:06
2007.12.30
Остановка двигателя HDD


9-1163886978
Vga
2006-11-19 00:56
2007.12.30
Паки в играх


15-1196281112
ANTPro
2007-11-28 23:18
2007.12.30
HP dv9000