Click to See Complete Forum and Search --> : CArray Question


August 23rd, 1999, 05:47 AM
Hello All,

I have encountered a problem in CArray which
I don't know the reason ..


// In my header file, i have a struct declaration
// File.h
typedef struct _tagStruct
{
int nCounter;
CString strName;
} REC_STRUCT;

CArray < REC_STRUCT*, REC_STRUCT* > m_Array;

// Then, in the implementation file
// File.cpp

void File::ArrayDemo()
{
m_Array.SetSize( 2 );
REC_STRUCT * p = NULL;

for ( int i=0; i<2; i++ )
{
p = new REC_STRUCT;
p->nCounter = 100; // Assign Values
p->strName = "HELLO";
m_Array->SetAt( i,p );

// Here, if I won't delete p, it will cause a
// memory leak. But if I will delete p, refer
// below :
delete p;
}

// Here if p is deleted, data in the array is lost !!
for ( int j=0; j<2; j++ )
{
REC_STRUCT * p1 = m_Array.GetAt( j );
// p1->nCounter has now a NEGATIVE value
// p1->strName is EQUAL to ""
}
}




I would appreciate if somebody can tell the reason
why CArray behaves like the one above!


Thank you very much!

Burlacu Ovidiu
August 23rd, 1999, 05:58 AM
Instead of:
CArray < REC_STRUCT*, REC_STRUCT* > m_Array;


why don't u use:
CArray < REC_STRUCT, REC_STRUCT& > m_Array;


?

Regards,
Ovidiu

fight to be the best of the rest because I am the best of the best -:)

AZur
August 23rd, 1999, 08:03 AM
You have create a array of pointers with an known type.

"p" is your placeholder for the memory address you get with "new REC_STRUCT".

With "m_Array->SetAt( i,p )" you hold the ADDRESS of your structure, not the content.

The CArray destructor only free's the space for the addresses (4 Byte) not your reference to the address.

To avoid the memory leak use this:



class CMyArray : public CArray < REC_STRUCT*, REC_STRUCT* >
{
public:
~CMyArray() { for (int nIdx = 0; nIdx < GetSize(); nIdx++) delete GetAt( nIdx ); }
};




Hint: if you call "ArrayDemo" more then once you must delete the old pointers (or reuse via "p = m_Array->GetAt( i )")

August 23rd, 1999, 08:38 PM
Hello Azur,

Thanks for ur reply!:-) I really appreciate it !:-)
However,with the code below,
my question actually is "How to release the
memory created by p = new REC_STRUCT;"

I understand now that m_Array->SetAt( i,p )
holds only the address of the structure.
If I "delete p", m_Array don't have the
contents now since the addresses it refers are
released! But as I understand "delete p"
is the only way to release the memory
created by "p = new REC_STRUCT;" ..

In conclusion, if I have to "delete p",
information will be lost. If I don't,
I'll end having memory leaks..

Kindly tell me if my understanding is
correct. Thank you very much!


void File::ArrayDemo()
{
m_Array.SetSize( 2 );
REC_STRUCT * p = NULL;

for ( int i=0; i<2; i++ )
{
p = new REC_STRUCT;
p->nCounter = 100; // Assign Values
p->strName = "HELLO";
m_Array->SetAt( i,p );
}
}

Wayne Fuller
August 23rd, 1999, 09:56 PM
Azur is right, but maybe didn't explain it to your content. The CArray only takes care of its own memory. If you allocate memory and assign that memory to the array, then you are responsible for releasing the memory. But you do not have to release it right when you add it to the array. Release it when you are through with it, like in the destructor as Azur suggested.

John Killingbeck
August 23rd, 1999, 09:59 PM
Here is my $0.00 worth. I don't understand why you choose to use a pointer to your structure instead of the structure itself. This simplifies life. Also if you create a pointer to your array and pass it to the function you can manipulate the array directly from the function. I had a similar situation in a program that I just wrote and handled it like this, your code base modified with my technique. I got the original technique from the book Mastering Visual C++ 4.0.

// File.h
typedef struct _tagStruct
{
int nCounter;
CString strName;
} REC_STRUCT;


// Then, in the implementation file
// File.cpp

// Inside CFile class definition
CArray < REC_STRUCT, REC_STRUCT > m_Array;
// Create pointer to array to pass to function
CArray < REC_STRUCT, REC_STRUCT >* m_pArray = &m_Array;

ArrayDemo( m_pArray );

void File::ArrayDemo( CArray < REC_STRUCT, REC_STRUCT >* pArray )
{
REC_STRUCT recMyStruct; // Declare a structure
pArray->SetSize( 2 );

for ( int i=0; i<2; i++ )
{
// Don't need new or delete, just reuse recMyStruct
recMyStruct.nCounter = 100; // Assign Values
recMyStruct.strName = "HELLO";
pArray->SetAt( i, recMyStruct );
// You can use the new and delete pair, just pointless.
// recMyStruct will be destroyed when it goes out of scope
}

for ( int j=0; j<2; j++ )
{
REC_STRUCT * p1 = &recMyStruct; // Reuse the structure variable
recMyStruct = m_Array.GetAt( j );
// p1->nCounter has now a NEGATIVE value
// p1->strName is EQUAL to ""
// check p1 now.
}
// Also watch your subscripts as you go. m_Array has 2 elements,
// indexes 0 and 1. I am not positive, but I think that you never reach
// index 2 in you loops. This can also cause faulty data as the
// program is reading uninitialized memory.
}



Pointers can cause confusion, especially when used with the new and
delete operators. You can lose track of just what you are creating
and deleting, the objects or the pointers.

August 24th, 1999, 01:21 AM
Thanks to all your replies !:-))

AZur
August 24th, 1999, 05:15 AM
First I preferred the way suggested by Burlacu Ovidiu and Wayne Fuller.

Second a answer how I understand your question about pointers.

In the array you hold addresses or in other words simple 4 byte values. Your pointer "p" is also simple a placeholder for a 4 byte value.

Excample:

typedef struct tagXYZ
{
DWORD nValue;
} XYZ, *PXYZ

CArray <PXYZ,PXY> arr;

// Address Content

PXYZ p = NULL; // 0x00010000 0x00000000

p = new XYZ; // 0x00010000 0x02000000

p->nValue = 0x123; // 0x02000000 0x123

arr.SetSize( 2 ); // 0x00300000 0x00000000
// 0x00300004 0x00000000

arr.SetAt( 0, p ); // 0x00300000 0x02000000

// now you can "forgot" the pointer because you have saved the address in Element 0 at address 0x00300000

// ...

// later:

PXYZ pOther = NULL; // 0x00015000 0x00000000

pOther = arr.GetAt( 0 ); // 0x00015000 0x02000000

// ... do somthing with "pOther"

arr.GetAt( 0 );