泄漏内存,释放对象和对象“复制”

时间:2013-07-15 09:14:35

标签: delphi delphi-xe3

这不是关于深度复制或克隆对象的问题,因为已经存在数百万个,尽管事实上我一开始就一直在寻找自己,如果这是我唯一的选择,可能会回归它。但对于像这样的简单事情来说,似乎不必要地过于复杂。

我一直在将一些C ++代码翻译成Delphi,基本上是根据this使用SDL公然复制俄罗斯方块。对于SDL来说,这是一次学习经历,但现在也是以Delphi的方式。我正在链接到具有我无法完全翻译的代码的特定页面。游戏按预期工作,但有一点我必须切换当前正在下降的块并将其方块添加到底部的其他块,然后将下一个块设置为当前下降的块。

以下是我的一些代码,但没有复制太多:TSingleSquareTBlockChangeFocusBlock程序。

TSingleSquare = class
  private
    S_TextureSurf: pSDL_SURFACE;
    S_BlockType: TBlockType;
    S_CenterX, S_CenterY: integer;
  public
    constructor Create(CX, CY: integer; T: pSDL_SURFACE; BT: TBlockType);
    destructor Destroy; override;
    procedure Draw(W: pSDL_SURFACE);
    procedure Move(D: TDirection);
    function GetCenterX: integer;
    function GetCenterY: integer;
    procedure SetCenterX(lX: integer);
    procedure SetCenterY(lY: integer);
  end;

Ttmp_ar = array of integer;
TSquaresArray = array of TSingleSquare;

TBlock = class
  private
    B_CenterX, B_CenterY: integer;
    B_BlockType: TBlockType;
    B_SquaresArray: TSquaresArray;
    B_TextureSurf: pSDL_SURFACE;
  public
    constructor Create(CX, CY: integer; TS: pSDL_SURFACE; BT: TBlockType);
    destructor Destroy; override;
    procedure SetupSquares(SX, SY: integer);
    procedure Draw(W: pSDL_SURFACE);
    procedure Move(D: TDirection);
    procedure Rotate;
    function GetRotatedSquares: Ttmp_ar;
    function GetSquaresArray: TSquaresArray;
    function GetBlockType: TBlockType;
  end;

procedure ChangeFocusBlock;
var
  Square_ar: TSquaresArray;
  i: integer;
begin
  Square_ar := g_FocusBlock.GetSquaresArray
  SetLength(g_OldSquares, (Length(g_OldSquares) + Length(Square_ar)));
  for i := Low(Square_ar) to High(Square_ar) do
    begin
      g_OldSquares[i+Length(g_OldSquares)-Length(Square_ar)] := Square_ar[i];
      Square_ar[i] := nil;
    end;
  g_FocusBlock := nil;
  g_FocusBlock := g_NextBlock;
  g_NextBlock := nil;
  g_FocusBlock.SetupSquares(BLOCK_START_X, BLOCK_START_Y);
  g_NextBlock := TBlock.Create(NEXT_BLOCK_CIRCLE_X, NEXT_BLOCK_CIRCLE_Y, GameBitmap, TBlockType(Random(11)));
end;

我知道我没有释放g_FocusBlock这就是我泄漏记忆的地方,但我找不到合适的地方来释放它,如果我这样离开,泄漏记忆是我唯一的问题。一旦我开始寻找一个解决方案,当我尝试迭代g_OldSquares并且我理解为什么时,它会让我陷入困境,这些方块仍然指向应该被释放的g_FocusBlock方块。当我在每个步骤添加断点并观察值发生了什么时,我的困惑就出现了,在g_OldSquares被释放后,g_FocusBlock被释放后,一切看起来都是顺序的,我将它设置为g_NextBlock并且创建一个新的下一个块。但是后来迭代g_OldSquares我注意到部分正方形被覆盖了,因为内存被重用了?我突然不相信Watchlist的值是真的吗?

本文的最后一行是,我想了解g_OldSquares发生了什么,以及为什么它的值不会改变,即使在通过监视列表检查时地址是相同的。

但更重要的是,正确的做法是什么才能让这项工作正常进行?我知道使用TPersistent并覆盖它的Assign以便能够在彼此之间实际分配对象(虽然我不完全确定如何实现这一点),但就在这里我仍然需要在g_OldSquaresg_FocusBlock之间摆脱“链接”或共享内存(我为任何错误使用的单词道歉,我不能真正流利地编写编程)释放后者。

让羞辱开始。

编辑:

执行此操作时发生AccessViolation

for i := Low(g_OldSquares) to High(g_OldSquares) do
  begin
    row := (g_OldSquares[i].GetCenterY - top) div row_size;
    Inc(squares_in_row[row])
  end;

constructor TBlock.Create(CX, CY: integer; TS: pSDL_Surface; BT: TBlockType);
var
  i: integer;
begin
  B_CenterX := CX;
  B_CenterY := CY;
  B_BlockType := BT;
  B_TextureSurf:= TS;

  case BT of
    SQUARE_BLOCK..REVERSE_S_BLOCK: SetLength(B_SquaresArray, 4);
    STRAIGHT_TRI_BLOCK..BENT_TRI_BLOCK: SetLength(B_SquaresArray, 3);
    TWO_BLOCK: SetLength(B_SquaresArray, 2);
    DOT_BLOCK: SetLength(B_SquaresArray, 1);
  end;

  SetupSquares(CX, CY);
end;

destructor TBlock.Destroy;
var
  i: integer;
begin
  for i := Low(B_SquaresArray) to High(B_SquaresArray) do
    B_SquaresArray[i].Free;
  inherited;
end;

1 个答案:

答案 0 :(得分:1)

http://aaroncox.net/tutorials/arcade/FallingBlocks6.html

引用您的来源
delete g_FocusBlock; // delete the current focus block
g_FocusBlock = g_NextBlock; // set the focus block to the next block
g_FocusBlock->SetupSquares(...);

// Set the next block to a new block of random type 
g_NextBlock = new cBlock(...);
好吧,然后就像它去的那样完全翻译它。

g_FocusBlock.Free; // delete the current focus block
g_FocusBlock := g_NextBlock; // set the focus block to the next block
g_FocusBlock.SetupSquares( ... );

// Set the next block to a new block of random type 
g_NextBlock := TBlock.Create(...);

你的C ++源代码中是否有像g_FocusBlock = NULL;这样的东西?不。那么你在Delphi源代码中发明的g_FocusBlock := nil;与实际程序无关,只是你的幻想。

如果您要基于refcounting memopry modelTInterfacedObject创建代码,但是您正在使用手动内存管理来翻译普通旧对象代码,那么这是有道理的,那么就完全可以完成C ++代码所做的事情。


建议:制作g_OldSquares类型的TList<TSingleSquare>TQueue<TSingleSquare>

然后你会做

g_OldSquares.AddRange(Square_ar);

或至少

for Square in Square_ar do g_OldSquares.Add(Square);

从该数组中移动项目。比具有索引变量和数组边界的所有体操更安全,您可以轻松地发现一些典型的“一个接一个”错误。

PS:TObjectList<T>选项OwnsObjects := True可能更容易编码。


现在,现在......

destructor TBlock.Destroy;
var
  i: integer;
begin
  for i := Low(B_SquaresArray) to High(B_SquaresArray) do
    B_SquaresArray[i].Free;
  inherited;
end;

在这里向我展示这段代码:http://aaroncox.net/tutorials/arcade/FallingBlocks2.html可以吗?

让我们看看你现在在做什么!

  1. 您创建了这些方块,并将指针放入TBlock.BSquares_Array
  2. procedure ChangeFocusBlock内,您将指向这些Square对象的指针移动到新容器中 - g_OldSquares
  3. 然后你尝试在TBlock.Destroy中销毁这些方块,尽管这些对象仍然有来自g_OldSquares的指针。
  4. 充其量是多余的,最坏的情况是危险的。现在我要求你展示g_FocusBlock.GetSquaresArray实施 - 那里发生了什么? 我要求你调试TBlock.Destroy并检查数组是否仍然包含任何非零值。


      

    执行此操作时发生AccessViolation

    (*1*)  for i := Low(g_OldSquares) to High(g_OldSquares) do
              begin
    (*2*)          row := (g_OldSquares[i].GetCenterY - top) div row_size;
    (*3*)          Inc(squares_in_row[row])
              end;
    

    以下是三行代码。 AV会产生哪一行? AV的这个具体细节是阅读还是写作以及在哪个位置?


    退出聊天记录

    delete g_OldSquares[index]; 
    g_OldSquares.erase(g_OldSquares.begin() + index); 
    index--; 
    

    我猜你最好把它重写为while循环(并修改变量),而不是引入容易出错的延迟nil压缩。

    实际上有一个巧妙的技巧 - 关于循环方向,当你可以删除元素时 - 但安全地避免enumeratinf一些元素两次或跳过另一个元素

    for i := TList.Count-1 downto 0 do 
      if SomeCondition then TList.Delete[i]; 
    

    使用此功能,您可以

    for j := Low(g_OldSquares) to High(g_OldSquares) do 
     if (g_OldSquares[j] <> nil) then 
       if ((g_OldSquares[j].GetCenterY - top) div row_size) = i then 
       begin 
         g_OldSquares[j].Free; 
         g_OldSquares[j] := nil; 
       end;
    

    并将其重构为

    for j := High(g_OldSquares) downto Low(g_OldSquares) do 
        if g_OldSquares[j].Row = i then 
           g_OldSquares.Delete(j); 
    

    假设g_OldSquares已转换为TObjectList<TSquare>类型且OwnsObjects属性设置为True