When writing a system involving a vector graphics operating system and a spatial data topology, I use Carray to store spatial data.
During the programming, I found a very misappropriate memory leak due to the function of over-trust Carray. Let's take a look at a class definition below:
Class cbreakpoint {
PUBLIC:
......
Double * xy; // the coordinate of these Breakpoint,
INT NID; // The global unique identifier of the perspective
......
}
CBREAKPOINT :: ~ CBreakPoint ()
{
IF (xy) delete [] xy;
XY = NULL;
}
The CBREAKPOINT class defines the folding point of the line, where xy represents the array of perspectives, we will apply for the memory space for XY when the specific input perspective; nnum represents the number of discount points in a fold line; NID expressed Unique identifier. Let us take a look at the definition of the folding line:
Class cbreakline {
PUBLIC:
......
CBREAKPOINT * M_PBREAKPOINT;
INT NID; // The global unique identifier of the fold
INT NVERTEXID1; // The start endpoint of the fold line
INT NVERTEXID2; // NiD of the endpoint of the fold line
INT nnum; // The number of discount points in the folding line
The state of the int State; // fold line, 0 represents isolated, 1 means communication, at least the status value is 0
......
}
The constructor and description functions of class CBreakline are as follows:
CBREAKLINE :: CBreakline ()
{
......
m_pbreakpoint = new cbreakpoint;
}
CBREAKLINE :: ~ cbereakline ()
{
......
IF (m_pbreakpoint) delete m_pbreakpoint;
m_pbreakpoint = NULL;
......
}
The CBreakLine class defines a basic spatial element - a fold line containing several perspectives, in the constructor of the CBREAKLINE class, we have the memory allocation for M_PBreakPoint:
CBREAKLINE :: CBreakline ()
{
......
m_pbreakpoint = new cbreakpoint;
......
}
With the definition of the discounted point and the folding, we can start managing our pain line. Because there is an uncertain N-fold line in a vector graph, it is necessary to manage these lines, we use the Carray template class. First define a collection class CBREAKLINESET, in the CBREAKLINESET class, define a Carray array member variable m_breaklises, which represent all the fold lines in the space. The definition of the class is as follows:
Class cbreaklineset {
PUBLIC:
......
Carray
M_BreakLines;
......
}
We can use Carray's member function add (arg_type newelement) to add an element (if necessary, extend array), using the subscript operator []. Other many member functions make us operate very easy, and because Carray is derived from COBject, it supports serialization so that we can save the fold line data to disk.
Now let's take a look at the three classes defined in front. The first layer is CBREAKPOINT, which represents the line's perspective, including the two endpoints of the folding line; the second layer is CBREAKLINE, which represents the fold line; the third layer is CBREAKLINESET Use it to unify the fold lines in all spaces in the system. Finally we define a collection management class CSETMANAGE: CLASS CSETMANAGE {
PUBLIC:
......
CBREAKLINESET LINES, TEMPLINES
Carray
Vertexid;
//
Used as a stack, deposit an endpoint ID during traversal
......
PUBLIC:
......
Void Trvel (int NfirstBreakPointID, CBREAKLINESET * PLINES)
......
}
It seems that everything is very smooth, now we can use Carray's member function to free operation M_Breaklines arrays. When the file is opened, all the fold line data is read into the object Breaklineset.
However, the problem has appeared.
In order to make statistical analysis of spatial data, the system requires all non-connected lines (ie, endpoints and other fold lines of the end points). We must determine if the fold line is connected according to the state of the fold. Because at any time, the status of the fold line is not 0, which requires a temporary space to store all the fold lines and set them to 0, and then start traversing. I adopted the following algorithm: copy all the lines in the LINES to Templines and delete all the lines in Lines. Then traversed the fold line in TemPlines, the state of the labeling line is 1, after the traversal is completed, copy all the status of 1 in TemPline to LINES, then delete all the data in Templines.
We use the following statement, please pay attention:
1for (int i = 0; i
{
Templines.m_breaklines.add (lines.m_breaklines [i]); //
Templines.m_breaklines [i] .state = 0;
}
Lines.m_breaklines.removeall (); // Clear all the lines in Lines
Depending on the system's other factors, we can select one endpoint of a fold line in TemPlines as a starting point, and then start traversing the entire fold line network in TemPlines. The recursive idea is used throughout, the specific function code is as follows:
INT g_nlines = TemPlines.m_breaklines.getsize (); // Number of folding lines in the system
Void CsetManage :: Travel (Int NfirstPoint)
{
INT NSECONDPOINTID; // NiD of the other end of the fold
For (INT I = 0; i IF (TemPlines.m_BreakLines [i] .state == 0 && (Templines.m_breaklines [i] .nvertexid1 == NfirstPointID || Templines.m_breaklines [i] .nvertexid2 == nfirstpointid)))) { Templines.m_breaklines [i] .state = 1; NSECONDPOINTID = Templines.GetothervertExid (NfirstPoint); / / NiD to another endpoint Vertexid.Add (NSecondPointID); Travel (nsecondpointid); IF (i == g_nlines) { IF (Vertexid.getsize ()> 0) { Vertexid.removeat (Vertexid.getsize () - 1); // Pop up a stack top element IF (Vertexid.getsize ()> 0) Travel (Vertexid [Vertexid.getsize () - 1]); Else Return; } Else Return; } Else Return; } } 2for (int i = 0; i { IF (TemPlines.m_BreakLines [i] .State == 1) Lines.m_breaklines.add (TemPlines.m_Breaklines [i]); // } g_nlines = lines.m_breaklines.getsize (); Templines.m_breaklines.removeall (); // Clear all temporary space Here, we start compiling programs, everything is normal when running, but an error occurs when you exit. Track debugging, discovery the problem out in the destructive function of CBREAKLINES, at this time, the content points to the M_PBREAKPOINT pointer has been unrecognizable. Is there a step? It is definitely repeatedly replicated in the fold line, during the deletion process. We know that Carray is adding a space for new items when adding an element, but it does not assign space for the pointer in the class (but the former program is the cbreaklineline) class, that is, the m_pbreakpoint-> xy pointer assignment New space. Therefore, it is said that when calling Templines.m_breaklines.add (lines.m_breaklines [i]); When the M_PBREAKPOINT in TemPlines is the same as the xy pointing to M_PBreakPoint in Lines, Lines.m_breaklines.removeall (); // Clear all the lines in Lines After the statement, the function layer calls the destructor function (cbreakpoint-> cbreakline-> cbreakli) NESET, at this time, TemPlines.m_breaklines [i] .M_PBreakPoint points to the content, but lines.m_breaklines [i] .m_pbreakpoint still exists, its value (the address pointed to by) retains the original address (this is very dangerous, Point to the space that is no longer it belongs to it). However, the operation below is still possible to perform normally because it does not involve the operation of XY. The description function of lines.m_breaklines [i] is called until you exit the program: IF (m_pbreakpoint) delete m_pbreakpoint; At this time, the danger broke out, and the unknown pointer caused memory leakage. It is very simple to find the root of the problem. We can overload the operator "=", replace the carray's add () function, allocate memory space for each m_pbreakpoint-> XY when the operator is overloaded, so that it and the original pointer points to different addresses, so when calling Removeall When it does not affect the data of the copied fold line. The overloaded function is simple, as follows: CBREAKLINE & CBREAKLINE :: Operator = (CBREAKLINE & P) { INT i = 0; ...... IF (m_pbreakpoint-> xy! = null) { Delete [] m_pbreakpoint-> xy; m_pbreakpoint-> xy = null; } Breakpoint-> XY = new double [nnum * 2]; For (i = 0; i { Breakpoint-> xy [i] = p.breakpoint-> xy [i]; } ...... RETURN * THIS; } Next, add Replace all the codes of all the red labels as follows: 1TemPlines.SetSize (g_nlines); For (int i = 0; i { Templines.m_breaklines [i] = lines.m_breaklines [i]; Templines.m_breaklines [i] .state = 0; } Add to Lines.setsize (g_nlines); Replace the original code as follows: INT K = 0; For (int i = 0; i { IF (TemPlines.m_BreakLines [i] .State == 1) Lines.m_breaklines [k ] = Templines.m_breaklines [i]; 2 } g_nlines = k; Thus, because we allocate a new space for all the new lines and the fold lines, there will be no obvious conflicts generated by memory space when allocating.