0

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

我一直在将一些 C++ 代码翻译成 Delphi,基本上是根据this公然使用 SDL 复制俄罗斯方块。就 SDL 而言,这是一次学习体验,但现在也是一种德尔福的方式。我正在链接到包含我无法完全翻译的代码的特定页面。游戏按预期运行,但有一点我必须切换当前正在下降的方块并将其方块添加到底部的其他方块中,然后将下一个方块设置为当前正在下降的方块。

这是我的一些代码,没有复制太多:TSingleSquareTBlock过程ChangeFocusBlock

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_FocusBlockg_NextBlockg_OldSquares我注意到部分方块被覆盖了,因为内存被重用了?我突然不能相信监视列表的值是真实的?

这面文字墙的底线是,我想了解发生了什么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;
4

1 回答 1

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(...);

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

如果您基于引用计数内存模型创建代码并且您正在使用手动内存管理转换普通旧对象代码,那么这可能是有意义的TInterfacedObject,那么只需执行 C++ 代码所做的事情。


建议:制作g_OldSquares一个TList<TSingleSquare>类型或可能TQueue<TSingleSquare>

然后你会做

g_OldSquares.AddRange(Square_ar);

或者至少

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

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

PS:TObjectList<T>with 选项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. 然后你尝试破坏这些正方形 intTBlock.Destroy尽管这些对象仍然有来自g_OldSquares.

这充其量是多余的,最坏的情况是危险的。现在我必须要求你展示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-loop(并修改变量),而不是引入容易出错的延迟 nil 挤压。

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

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.

于 2013-07-15T09:25:15.127 回答