2013-07-15 66 views
0

这不是关于深拷贝或克隆对象的问题,因为已经有数百万的对象,尽管事实上这就是我一开始就在寻找自己的事实,如果这是我唯一的选择,请重新登录。但似乎没有必要像这样简单的事情过分复杂。泄漏内存,释放对象和对象“复制”

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

下面是我的一些代码,没有太多复制:TSingleSquare,TBlockChangeFocusBlock程序。

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_FocusBlock被释放后,我将它设置为g_NextBlock并创建一个新的下一个块后,所有内容都按g_OldSquares顺序排列。然而,后来迭代g_OldSquares我注意到,部分正方形正在被覆盖,因为在内存得到重用?我突然不能相信Watchlist的值是真的?

这段文字的底线是,我想了解g_OldSquares发生了什么,以及为什么它的值不会改变,即使通过监视列表检查时地址是相同的。

但更重要的是什么才是正确的行动,以使其正常工作?我知道使用TPersistent并覆盖它的Assign是为了能够在对方之间真正地分配对象(尽管我不完全确定如何完成这个任务),但在这里我仍然必须摆脱“链接”或在g_OldSquaresg_FocusBlock之间共享内存(我对任何不正确使用的单词表示歉意,我并不真正流利地使用编程),以便能够释放后者。

让羞耻开始。

编辑:当我做这个

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; 
+0

您可以查看“TInterrfacedObject”http://en.wikipedia.org/wiki/Reference_counting –

+1

您可以创建一个对象并对其进行引用。然后,稍后,您放弃对该目标的引用,并使引用指向另一个对象。正是在这一点上,就在你放弃物体之前,你需要释放它。是的,使用接口进行自动引用计数可以完成这项工作。 –

+0

如果没有任何其他链接到对象(如果你说关于内存泄漏,那么可能没有)然后'g_FocusBlock:= nil;'可能是删除最后一个链接,因此应该替换为'g_FocusBlock.Free '。但是,如果你使用引用计数的内存模型,那么'g_FocusBlock:= nil;'会(这是最后一个指针)真正为你释放对象。 –

回答

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

以及引用您的来源发生

AccessViolation,然后就像它去准确翻译。

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++源代码anythign像g_FocusBlock = NULL;?没有。那么你在Delphi源码jst中发明的g_FocusBlock := nil;并不涉及到实际的程序,而只是你的幻想。

如果您要根据refcounting memopry modelTInterfacedObject创建代码,但是您正在使用手动内存管理翻译普通旧对象代码,那么只需执行C++代码的功能即可。


建议:做一个TList<TSingleSquare>g_OldSquares也许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. 然后你试图摧毁这些广场的int尽管这些对象仍然有来自g_OldSquares的指针。

这是多余的,最坏的情况是危险的。现在我不得不要求你展示g_FocusBlock.GetSquaresArray实现 - 那里发生了什么? 而我要求你调试TBlock.Destroy并检查数组中是否有任何错误仍然包含任何非零值。当我这样做

(*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; 

这里有三行代码发生


AccessViolation。哪一行产生AV? 这是什么AV的细节,它是阅读还是书写以及在哪个位置?


迁出聊天备案

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

我想你最好还是将它改写为while循环(和修改变量),而不是引入容易出错延迟零挤压。

居然还有一个绝招 - 事情是有关环路方向,当你可以删除的元素 - 而安全地避免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

+0

如果我这样做,只要块碰到地面,我会立即得到一个AccessViolation错误,实际上稍后,当我遍历g_OldSquares时会出现AccessViolation错误。是的,我同意,它没有多大意义,但它是为了我自己的理智,我需要它,因为我无法释放它... – Raith

+0

然后你应该调试该AV。它出现的确切声明是什么,以及确切的错误文字细节是什么? PS。你告诉“泄漏的内存是我唯一的问题”,但现在ti显示你所说的是不真实的:-(这将唉,影响信任你说的其他细节... –

+0

显示你的代码TBlock的构造函数和析构函数。看到原代码没有任何析构函数!http://aaroncox.net/tutorials/arcade/FallingBlocks2.html –