Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 02.04.2012, 16:14
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Добавление записи в динамический массив

Здравствуйте.
Столкнулся с некоторыми проблемами при создании объекта и массива этого-же объекта, конкретно - с манипуляцией элементов в массиве.
Объект содержит только энный набор данных и несколько функций "одиночной" обработки.
Код:
type

  TCommand = class (TObject)
    ComList: TStringList;
    Error: Cardinal;
  public
    procedure AssignObjects (ObjArray: array of TObject) ;
    procedure AssignStrings (Str: TStrings);
    procedure AssignObjectsStrings (Str: TStrings);
    constructor Create;
    destructor Free;
  end;
Далее формируются запчасти для массива и сам массив:
Код:
  TCommandStackElement= record
  Command: TCommand;
  Time: TDateTime;
  end;

  TCommandStackElmt = array of TCommandStackElement;
  PCommandStackElmt = ^TCommandStackElmt;


  TCommandStack = class (TObject)
  private
    PCommandStack: PCommandStackElmt;
    Created: Boolean;  //проверка на "созданность"
    FSize: Cardinal;     //под сколько элементов уже выделено памяти
    FCount: Cardinal;  //количество занятых элементов
  public
    function AddNew (ICommand: TCommand): Boolean;
//и остальные функции обработки
На данный момент выделение памяти делаю по блочно (FSize всегда кратно 256 и не меньше FCount)
Код:
ReallocMem(PCommandStack,(FSize)*SizeOf(TCommandStackElement));
Вот здесь возникает 1я проблема - при быстром добавлении элементов, программа не успевает выделить очередной блок памяти и обработать текущую, что ведет к ошибкам. Как можно реанимировать пациента? (вводить в клас дополнительный буллин на состояние ReallocMem, или есть другие методы?)

2я проблема в добавлении элемента - для этого вызывается AddNew (ICommand: TCommand):
Код:
function TCommandStack.AddNew (ICommand: TCommand): Boolean;
var OldLength: Cardinal;
begin
 try
  Result:=True;
  if FSetSize(Self.FCount+1) then
  begin
    Inc(FCount);
    try
      PCommandStack^[Self.FCount].Time:=Now;
      PCommandStack^[Self.FCount].@Command:=@ICommand;
    except
      Result:=False;
      PCommandStack^[Self.FCount].Command:= TCommand.create;
      PCommandStack^[Self.FCount].Command:=ICommand;
    end;
  end;
 except
 end;
end;
Собственно конкретно эта конструкция неверна - PCommandStack^[Self.FCount].@Command:=@ICommand; явно дико воспринимается компилятором (аналог в эксепте - работает). Задумка передачи указателя - после передачи элемента в массив, он все равно тут-же освобождается. Соответственно проще отдать "руль", а не создавать с 0ля. Весь вопрос как это сделать - создавать массив указателей:
Код:
  PCommandStackElement = ^TCommandStackElement;
  TCommandStackElmt = array of ^PCommandStackElement;
  PCommandStackElmt = ^TCommandStackElmt;


  TCommandStack = class (TObject)
  private
    PCommandStack: PCommandStackElmt;
или есть какая-нибудь хитрая функция/процедура?

Последний раз редактировалось Velz, 02.04.2012 в 16:19.
Ответить с цитированием
  #2  
Старый 02.04.2012, 17:14
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

Если собрались использовать array of TCommandStackElement, то надо так
PCommandStackElmt = ^TCommandStackElmt; - выкинуть.

Код:
TCommandStack = class (TObject)
  private
    PCommandStack: TCommandStackElmt;//<<==-- здесь так
    Created: Boolean;  //проверка на "созданность"
    FSize: Cardinal;     //под сколько элементов уже выделено памяти
    FCount: Cardinal;  //количество занятых элементов

вместо
Код:
ReallocMem(PCommandStack,(FSize)*SizeOf(TCommandStackElement));
надо
Код:
SetLength(PCommandStack,FSize);

Убрать ^ при обращении к PCommandStack.

и что Вы хотели сделать здесь
Код:
      
      PCommandStack[Self.FCount].@Command:=@ICommand;
и особенно здесь
Код:
      
PCommandStack[Self.FCount].Command:=TCommand.create;
// сначала записываем в поле указатель на только что созданный класс
PCommandStack[Self.FCount].Command:=ICommand;
// а затем затираем его

Если Вы вот этим кодом @Command:=@ICommand; хотите побайтно скопировать содержимое объекта TCommand - то хочу разочаровать - ничего не выйдет.
И немножко ликбеза.
Экземпляры классов в дельфи - всегда указатели.
Копирующего конструктора и поэлементного присваивания по умолчанию для классов НЕТ !. Если они нужны - нужно делать самому. Для некоторых классов копирование содержимого имеется(например TStringList, TBitmap), но у этих классов есть метод Assign, в котором прописано - что и как копируется/присваивается.
Удачи.
Ответить с цитированием
Этот пользователь сказал Спасибо icWasya за это полезное сообщение:
Velz (03.04.2012)
  #3  
Старый 03.04.2012, 08:23
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

К сожалению, использовать SetLength не выходит - установка таким образом длинны массива происходит только 1 раз, потом не меняет (не знаю с чем это связанно), поэтому и пришлось переходить на указатели и использовать выделение памяти. Поэтому в данном случае от PCommandStackElmt = ^TCommandStackElmt; избавиться не удается =(

Код:
PCommandStack[Self.FCount].Command:=TCommand.create;
// сначала записываем в поле указатель на только что созданный класс
PCommandStack[Self.FCount].Command:=ICommand;
// а затем затираем его
Хммм, действительно пересоздавать не надо, спасибо.

Код:
PCommandStack[Self.FCount].@Command:=@ICommand;
В теории, элемент уже создан (где то заранее и передан через AddNew (ICommand: TCommand)), поэтому и была мысля "подменить" указатель, чтобы по возможности минимизировать время обработки, но видимо, нужно будет массив указателей.

Таким образом, остался только вопрос с "безопасной" работой памяти в ReallocMem (ибо избавиться от него не удается). Пытался сделать конструкцию с GetMemory и дополнительным указателем, с последующим копированием данных и переприсвоением указателя:
Код:
GetMem(RecPnt,(BufSize)*SizeOf(TCommandStackElement));
if PCommandStack<>nil then
 begin
   Move(PCommandStack^,RecPnt^,SizeOf(TCommandStack));
   FreeMem(PCommandStack, FSize * SizeOf(TCommandStack));
 end;
PCommandStack := RecPnt;
Но даже эта конструкция не устойчива к быстрой скорости обработки информации.
Ответить с цитированием
  #4  
Старый 03.04.2012, 09:19
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

>установка таким образом длинны массива происходит только 1 раз
Что значит не меняет, - это как Вы делаете?
А если хотите работать через GetMem, RealocMem, FreeMem? то нельзя использовать динамический массив (array of)

>нужно будет массив указателей
А как я уже говорил в Дельфи экземпляры классов итак указатели.
Ответить с цитированием
Этот пользователь сказал Спасибо icWasya за это полезное сообщение:
Velz (03.04.2012)
  #5  
Старый 03.04.2012, 09:55
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от icWasya
>
Что значит не меняет, - это как Вы делаете?
А если хотите работать через GetMem, RealocMem, FreeMem? то нельзя использовать динамический массив (array of)
изначально был array of TCommandStackElement, и с ним использовался SetLength. Первый прогон установки длинны проходил на ура (SetLength(TCommandStackElmt,FCount); где TCommandStackElmt =array of TCommandStackElement), а вот последующие никак не хотели ее менять. С чем это связанно понять не смог, поэтому перешел на указатель на объявленный массив и соответственно GetMem/RealocMem, FreeMem.

Цитата:
Сообщение от icWasya
>
А как я уже говорил в Дельфи экземпляры классов итак указатели.
Т.е. PCommandStack[Self.FCount].@Command:=@ICommand; аналогично PCommandStack[Self.FCount].Command:=ICommand; ?

Последний раз редактировалось Velz, 03.04.2012 в 09:59.
Ответить с цитированием
  #6  
Старый 03.04.2012, 10:41
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

вот это PCommandStack[Self.FCount].@Command вообще не должно скомпилироваться,

А вот это
PCommandStack[Self.FCount].Command:=ICommand;
PCommandStack[Self.FCount].Command:=@ICommand;
одно и то же.

И ещё раз: если работаете с GetMem, ReallocMem, FreeMem, то нельзя использовать динамический массив.
Надо тогда делать так

TCommandStackElmt = array[0 .. MaxInt div 4-1] of TCommandStackElement;
PCommandStackElmt = ^TCommandStackElmt;

и не использовать SetLength никогда
Ответить с цитированием
  #7  
Старый 03.04.2012, 13:02
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от icWasya
И ещё раз: если работаете с GetMem, ReallocMem, FreeMem, то нельзя использовать динамический массив.
Надо тогда делать так

TCommandStackElmt = array[0 .. MaxInt div 4-1] of TCommandStackElement;
PCommandStackElmt = ^TCommandStackElmt;

и не использовать SetLength никогда

GetMem, ReallocMem, FreeMem и SetLength использовал только в различных типах данных (1е с указателем на массив, 2е при работе непосредственно с массивом).

Вопрос: TCommandStackElmt = array[0 .. MaxInt div 4-1] of TCommandStackElement; это фиксированный массив по константе? Если да, то возможно ли сделать динамический массив из PCommandStackElmt? Или вообще сделать динамический массив указателей:
Код:
  TCommandStackElmt = ^TCommandStackElement;
  PCommandStackElmt = array of TCommandStackElmt;
И работать с ним(PCommandStackElmt) через SetLength или GetMem, ReallocMem, FreeMem?
Ответить с цитированием
  #8  
Старый 03.04.2012, 13:09
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

я же говорю
TCommandStackElmt = array[0 .. MaxInt div 4-1] of TCommandStackElement;
PCommandStackElmt = ^TCommandStackElmt;
Размер указан толькол для обмана компилятора - что бы не ругался на границу массива. Вы же нигде не будете использовать переменные типа TCommandStackElmt.( я надеюсь
а работать так

Код:
PCommandStack : PCommandStackElmt;

GetMem(PCommandStack , FCount*sizeof(TCommandStackElement));
ReallocMem(PCommandStack , FCount*sizeof(TCommandStackElement));
FreeMem(PCommandStack);

Ещё раз для тех кто в танке
если написали
Код:
PCommandStackElmt = array of TCommandStackElmt
, то используйте только SetLength, иначе потом можно огрести по полной
Ответить с цитированием
  #9  
Старый 03.04.2012, 13:42
Pyro Pyro вне форума
Так проходящий
 
Регистрация: 18.07.2011
Сообщения: 805
Версия Delphi: 7Lite
Репутация: 6063
По умолчанию

ObjectList из модуля Contnrs и никаких проблем
только надо свойство OwnsObjects иметь ввиду, если не надо, чтобы он сам освобождал элементы, при их удалении

я бы сделал так:
PHP код:
uses Contnrs

TCommandStack 
= class (TObjectList)

TCommand = class (TStringList
Ответить с цитированием
  #10  
Старый 03.04.2012, 16:28
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от icWasya
я же говорю
TCommandStackElmt = array[0 .. MaxInt div 4-1] of TCommandStackElement;
PCommandStackElmt = ^TCommandStackElmt;
Размер указан толькол для обмана компилятора - что бы не ругался на границу массива.
а работать так

Код:
PCommandStack : PCommandStackElmt;

GetMem(PCommandStack , FCount*sizeof(TCommandStackElement));
ReallocMem(PCommandStack , FCount*sizeof(TCommandStackElement));
FreeMem(PCommandStack);

Ещё раз для тех кто в танке
если написали
Код:
PCommandStackElmt = array of TCommandStackElmt
, то используйте только SetLength, иначе потом можно огрести по полной

Уххх... в общем получилось все сделать
Итоговая конструкция осталась изначальной:
Код:
  TCommandStackElmt = array of TCommandStackElement;
  PCommandStackElmt = ^TCommandStackElmt;
Обманывать компилятор с помощью [0 .. MaxInt div 4-1] не нашлось необходимости, все отлично работает:
Увеличение массива происходит через SetLength(PACommandStack^,FCount); присвоение данных через PACommandStack^[FCount-1].Command:=ICommand; так-же без проблем.
А косяк обнаружился в теле основной программы - после выполнения AddNew (ICommand: TCommand), технично вызывался деструктор того, что уходило в ICommand...
В общем весь косяк был в том, что я изначально не знал о "указательной" природе классов, и удалял тот-же самый элемент, который только только забил в массив.

P.S. Называется хотел как лучше (раз создал объект - будь добр в конце удалить, дабы не мешался в памяти), а получилось через ...

Спасибо
Ответить с цитированием
  #11  
Старый 03.04.2012, 16:45
icWasya icWasya вне форума
Местный
 
Регистрация: 09.11.2010
Сообщения: 499
Репутация: 10
По умолчанию

Ну так вернёмся к моему первому ответу - если SetLength заработал, тогда
вот это PCommandStackElmt = ^TCommandStackElmt; - не нужно.
используй прямо
Код:
PCommandStack : TCommandStackElmt;
Ответить с цитированием
  #12  
Старый 03.04.2012, 16:58
Velz Velz вне форума
Прохожий
 
Регистрация: 02.04.2012
Сообщения: 6
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от icWasya
Ну так вернёмся к моему первому ответу - если SetLength заработал, тогда
вот это PCommandStackElmt = ^TCommandStackElmt; - не нужно.
используй прямо
Код:
PCommandStack : TCommandStackElmt;
Ага, как раз проверял - все работает... и даже напрямую (объявление массива в самом классе
Код:
  TCommandStack = class (TObject)
  private
    Created: Boolean;
    FSize: Cardinal;
    FCount: Cardinal;
    function GetLength : Cardinal;
    function FSetSize (NewSize: Cardinal) : Boolean;
    procedure FSetCount (NewCount: Cardinal);
  public
    CommandStack: array of TCommandStackElement;
Все отлично, вся проблема только из за "указательной" природы классов, вот и была туча различных ошибок.

P.S. Деструктор все-равно делать, вот только в какой момент и к кому его прикручивать... но это уже другая тема =)

Последний раз редактировалось Velz, 03.04.2012 в 17:01.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 02:39.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025