Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Стек из объектов класса


Автор: K0T9I 27.9.2010, 00:56
Код


TSampleType = (stNone,...);

TSample = class
  private
    FContent:string;
    FSType:TSampleType;
...
  public
    property Content:string read FContent;
    property SType:TSampleType read FSType;
end;

TSampleArray = array of TSample;

TSampleList = class
  private
    FFields:TSampleArray;
    FCount:integer;
    function GetFields(i: integer): TSample;
....

  public
    property Count:integer read FCount;
    property Fields[i:integer]:TSample read GetFields;default;
end;

TSampleStack = class
    private
      FStack:TSampleList;
      FCount:integer;
    public
      function Push(Sample:TSample):integer;overload;
      function Pop:TToken;
....
end;

..........

implementation

function TSampleList.Last: TSample;
begin
  result:=FFields[FCount-1];
end;

procedure TSampleList.Delete(Index: integer);
begin
  dec(FCount);
  FreeAndNil(FFields[Index]);
  if Index<FCount then move(FFields[Index+1],FFields[Index],(FCount-Index)*sizeof(TSample));
  SetLength(FFields,FCount);
end;

function TSampleStack.Pop: TSample;
begin
  if FStack.Count>0 then
  begin
    result:=FStack.Last; // возвращаем ссылку на последний элемент массива FStack
    FStack.Delete(FStack.Count-1); // а потом его же и удаляем, то есть по сути вернется nil :(
    dec(FCount);
  end
end;

.....

procedure TForm1.Button1Click(Sender: TObject);
begin
  stack:=TSampleStack.Create;
  stack.Push('test',stNone);
  form1.Caption:=stack.Pop.Content; // access violation..., насколько я понял, потому что объект уже удален.
end;


я не могу понять, как организовать метод Pop для стека с объектами классов.
если методом Pop возвращать ссылку на объект, то вернется nil
если в Pop предварительно копировать возвращаемый объект и возвращать уже копию, то как потом ее удалить?

Код

function TSampleStack.Pop: TSample;
var Sample:TSample;
begin
  if FStack.Count>0 then
  begin
    Sample:=TSample.Create;
    Sample.Content:=FStack.Last.Content;
    Sample.SType:=FStack.Last.SType;
    result:=Sample;// так все вернется нормально, но произойдет утечка памяти, так как объект Sample не удаляется
    FStack.Delete(FStack.Count-1);
    dec(FCount);
  end
end;


Автор: bems 27.9.2010, 01:13
Вернется точно то та ссылка, которая была в списке до удаления, а не nil.
Чтобы она оставалась валидной, добавь классу TSampleList метод Extract, который будет удалять элемент из списка, но не будет удалять его (таким образом Delete сведется к Extract и Free). Ну и Pop будет вызывать Extract

Добавлено @ 01:18
А за освобождение должен отвечать код, вызывающий рор.

Добавлено @ 01:28
Код

function TSampleList.Extreact(Index: integer): TSample;
begin
  Dec(FCount);
  Result := FFields[Index];
  if Index<FCount then move(FFields[Index+1],FFields[Index],(FCount-Index)*sizeof(TSample));
  SetLength(FFields,FCount);
end;

procedure TSampleList.Delete(Index: integer);
begin
  Extract(Index).Free
end;

function TSampleStack.Pop: TSample;
begin
  if FStack.Count>0 then
  begin
    result := FStack.Extract(FStack.Count-1); 
    dec(FCount);
  end else result := nil;
end;

Автор: K0T9I 27.9.2010, 02:17
bems, спасибо, натолкнул на мысль.

Код

TSampleStack = class
    private
...
      FPopItem:TSample;
      function ReadPopItem: TSample;
    public
...
      function Pop:TSample;
...
      constructor Create;overload;
      destructor Destroy;override;
      property PopItem:TSample read ReadPopItem;
...
  end;

constructor TSampleStack.Create;
begin
  inherited;
  FStack:=TSampleList.Create;
  FPopItem:=TSample.Create;
  Clear;
end;

destructor TSampleStack.Destroy;
begin
  Clear;
  FreeAndNil(FStack);
  FreeAndNil(FPopItem);
  inherited;
end;

function TSampleStack.Pop:TSample;
begin
  if FStack.Count>0 then
  begin
    FPopItem.Assign(FStack.Last);
    result:=FPopItem;
    FStack.Delete(FStack.Count-1);
    dec(FCount);
  end
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  stack:=TSampleStack.create;
  stack.Push('test',stNone);
  form1.Caption:=stack.Pop.Content;
  stack.Free;
end;


тему можно закрывать.

Автор: bems 27.9.2010, 02:25
Это не решение. Что будет если вызывающий код вызовет Рор, когда результат предыдущего вызова еще используется?

Автор: K0T9I 27.9.2010, 02:34
Цитата(bems @ 27.9.2010,  02:25)
Это не решение. Что будет если вызывающий код вызовет Рор, когда результат предыдущего вызова еще используется?

будет то, что результат предыдущего вызова станет равен результату нового вызова.
я не ищу универсального решения, в моем случае Pop вызывается снова только по окончании обработки предыдущего вызова.
а на крайний случай:
Код

OldPop:=TSample.Create;
OldPop.Assign(stack.Pop);
summary:=OldPop.Content+stack.Pop.Content;
OldPop.Free
stack.Free;

Автор: bems 27.9.2010, 02:40
Стандартное поведение в таких случаях - при Push стек принимает объект во владение, при Pop - передаёт во владение вызвавшему.
Но как хочешь...

Автор: K0T9I 27.9.2010, 02:49
Цитата(bems @ 27.9.2010,  02:40)
при Pop - передаёт во владение вызвавшему

хм, чего не знал, того не знал. гугл выдает малозначительные примеры и описание подобное "первый зашел - последний вышел"
спасибо еще раз, буду думать. тему все равно можно закрыть

Автор: bems 27.9.2010, 03:00
Ну я немного неверно выразился. Правильно: _если_ при Push стек принимает объект во владение, _то_ при Pop - передаёт во владение вызвавшему. Поскольку у тебя при Delete в списке делается Free, то я делаю вывод, что твой список владеет объектами.
А тему закрывать ни к чему, просто пометь вопрос решенным

Автор: Qu1nt 27.9.2010, 09:35
Код

// Delphi < 2009

uses
  Contnrs;

TObjectStack

// Delphi >= 2009

uses
  Generics.Collections;

TStack<TObject>

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)