delphi - Leaking memory, freeing objects and object "copying" -


this not question deep copying or cloning objects, there's millions of already, despite fact that's i've been looking myself @ first , fall on if option. seems unnecessarily overcomplicated simple thing this.

i've been translating c++ code delphi, blatantly copying tetris using sdl according this. it's been learning experience, in regard of sdl, in delphi kind of way. i'm linking particular page has code cannot translate. game works intended, there comes point have switch block falling , add squares others @ bottom, , set next block falling one.

here's of code without copying much: tsinglesquare, tblock , changefocusblock procedure.

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)));   := low(square_ar) high(square_ar)     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; 

i know i'm not freeing g_focusblock , i'm leaking memory, can't find appropriate place free in first place , if leave this, leaking memory issue. once started looking solution, bit me in ass later when try iterate on g_oldsquares , understand why, squares still pointing g_focusblock squares supposed freed. confusion comes in when add breakpoints @ every step , observe happening values, seems in order g_oldsquares after g_focusblock freed, after set g_nextblock , create new next block. later iterating on g_oldsquares notice part of squares getting overwritten, in memory getting reused? cant trust watchlist values true?

bottom line of wall of text understand happening g_oldsquares , why it's values not change thought addresses same when inspecting via watchlist.

but more importantly proper course of action make work properly? aware of using tpersistent , overriding it's assign able assign objects between each other (although i'm not sure how accomplish either), right here still have rid of "link" or shared memory (i apologize incorrectly used words, i'm not fluent in programming) between g_oldsquares , g_focusblock able free latter.

let shaming begin.

edit:

accessviolation occurs when this

for := low(g_oldsquares) high(g_oldsquares)   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   := low(b_squaresarray) high(b_squaresarray)     b_squaresarray[i].free;   inherited; end; 

quoting sources http://aaroncox.net/tutorials/arcade/fallingblocks6.html

delete g_focusblock; // delete current focus block g_focusblock = g_nextblock; // set focus block next block g_focusblock->setupsquares(...);  // set next block new block of random type  g_nextblock = new cblock(...); 

well, translate goes.

g_focusblock.free; // delete current focus block g_focusblock := g_nextblock; // set focus block next block g_focusblock.setupsquares( ... );  // set next block new block of random type  g_nextblock := tblock.create(...); 

is there anythign g_focusblock = null; in c++ sources? no. invented g_focusblock := nil; in delphi source jst not relate actual program , fantasy.

it coul make sense if create code based on refcounting memopry model , tinterfacedobject you're translating plain-old-objects code manual memory management, c++ code does.


suggestion: make g_oldsquares of tlist<tsinglesquare> type or maybe tqueue<tsinglesquare>.

then

g_oldsquares.addrange(square_ar); 

or @ least

for square in square_ar g_oldsquares.add(square); 

to move items array. more safe gymnastics index variables , array boundaries, can stumble upon typical "off one" error.

ps: tobjectlist<t> option ownsobjects := true might easier code against.


now, now...

destructor tblock.destroy; var   i: integer; begin   := low(b_squaresarray) high(b_squaresarray)     b_squaresarray[i].free;   inherited; end; 

show me code here: http://aaroncox.net/tutorials/arcade/fallingblocks2.html can you?

let doing now!

  1. you created squares , put pointers them tblock.bsquares_array
  2. inside procedure changefocusblock move pointers square objects new container - g_oldsquares
  3. then try destroy squares int tblock.destroy despite objects still have pointers g_oldsquares.

that redundant @ best , dangerous @ worst. have demand show g_focusblock.getsquaresarray implementation - happen there ? , demand debug tblock.destroy , check if array mistake still contains non-nil value.


accessviolation occurs when this

(*1*)  := low(g_oldsquares) high(g_oldsquares)           begin (*2*)          row := (g_oldsquares[i].getcentery - top) div row_size; (*3*)          inc(squares_in_row[row])           end; 

here 3 lines of code. line result av ? specifics of of av, reading or writing , @ location ?


moving out of chat record

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

i guess you'd better rewrite while-loop (and modifying variable) instead of introducing bug-prone delayed nil squashing.

actually there neat trick - thing loop direction, when can delete element - safely avoiding enumeratinf element twice or skip element

for := tlist.count-1 downto 0    if somecondition tlist.delete[i];  

using can take

for j := low(g_oldsquares) high(g_oldsquares)   if (g_oldsquares[j] <> nil)     if ((g_oldsquares[j].getcentery - top) div row_size) =     begin       g_oldsquares[j].free;       g_oldsquares[j] := nil;     end; 

and refactor

for j := high(g_oldsquares) downto low(g_oldsquares)      if g_oldsquares[j].row =         g_oldsquares.delete(j);  

assuming g_oldsquares converted tobjectlist<tsquare> type ownsobjects property set true.


Comments