What's the proper way to clean up this type of memory allocation (C++)

SunnyD

Belgian Waffler
Jan 2, 2001
32,674
146
106
www.neftastic.com
Assume you have:

...
TYPEDEFD_TYPE * pointerToTYPE = (TYPEDEFD_TYPE*) new unsigned char[Multiple * sizeof(TYPEDEFD_TYPE)];
...

How do you go about properly deleting it? Technically, you would "delete [] pointerToTYPE", but given it's actually allocated as an array of unsigned chars, it doesn't feel correct to me. Simply doing "delete pointerToType" also doesn't feel right as there is possibly multiple elements of TYPEDEFD_TYPE in the allocation.

Would it be proper to say "delete [] (unsigned char*)pointerToTYPE"?

Of course I should just use malloc/free, that would simplify things a bit.
 

dighn

Lifer
Aug 12, 2001
22,820
4
81
delete [ ] (unsigned char*)pointer; looks correct to me, but how about just

TYPE *pointer = new TYPE[ Multiple ];
delete [ ] pointer



 

SunnyD

Belgian Waffler
Jan 2, 2001
32,674
146
106
www.neftastic.com
Originally posted by: dighn
delete [ ] (unsigned char*)pointer; looks correct to me, but how about just

TYPE *pointer = new TYPE[ Multiple ];
delete [ ] pointer

Because - in the case where you have something like:

struct TYPE2 {
...
};

struct TYPE1 {
int Count;
TYPE2 ArrayofTYPE2[1]; // Windows APIs use this alot...
};

The thing is that the Windows API uses this format a lot, returning basically variable sized structures to you. You allocate a buffer large enough, and cast into it. It's annoying, but they did what they had to do with the standards when they created the API's. Note, these would have originally been allocated with malloc, so this wouldn't be an issue. However I'm stubborn and want to use new instead. :)
 

Venix

Golden Member
Aug 22, 2002
1,084
3
81
Yes, you must call delete/delete[] with the same type as new/new[]. Using the wrong type will probably work if the type has no destructor, but it is still a very bad idea.

I would do the following:

std::vector<unsigned char> buffer(size);
SomeType* ptr = reinterpret_cast<SomeType*>(&buffer[0]);

Or if the array must be passed out of the function:

struct Delete
{
template <typename T>
void operator()(T t)
{
delete[] reinterpret_cast<unsigned char*>(t);
}
};

boost::shared_ptr<SomeType>((SomeType*)new unsigned char[size], Delete());

This removes the nasty, error-prone cast required when calling delete[], and greatly reduces the risk of memory leaks.
 

Templeton

Senior member
Oct 9, 1999
467
0
0
std::auto_ptr<TYPEDEFD_TYPE> pointerToTYPE((TYPEDEFD_TYPE*)::eek:perator new(Multiple * sizeof(TYPEDEFD_TYPE)));

use boost::scoped_ptr or boost::shared_ptr if available depending on needs.
 

SunnyD

Belgian Waffler
Jan 2, 2001
32,674
146
106
www.neftastic.com
You guys don't seem to understand, or I didn't explain it correctly - either way... as per the previous sample:

struct TYPE2 {
...
};

struct TYPE1 {
int Count;
TYPE2 ArrayofTYPE2[1]; // Windows APIs use this alot...
};


Basically, you're declaring an instance of TYPE1 in your code. The problem is, TYPE2 is the "Variable" part of the structure in TYPE1. This is used extensively in Win32 because of the standards for C at the time Win32 was made - there was no way to declare a variable sized array.

The typical code would be something like:

TYPE1 *pType1 = (TYPE1*)malloc(sizeof(TYPE1) + (Multiple * sizeof(TYPE2)));
...
free(pType1);


Translating this to using new, you would obviously have to do the following in order to maintain a contiguous block of memory:

TYPE1 *pType1 = (TYPE1*) new unsigned char[sizeof(TYPE1) + (Multiple * sizeof(TYPE2))];

The question then becomes, how do you delete that pointer? I assume because it was created as an array of contiguous unsigned chars, you would want:

delete [] (unsigned char*)pType1;

I'm just not entirely certain of the behavior of the delete operator when it comes to this type of operation. Whereas free() would simply deallocate the entire allocated block as allocated by the memory manager, delete's constructs are designed to handle objects, which leads me to believe that I have to cast the pointer back to it's allocated original type in order to properly dispose of the memory.

Sorry if I wasn't clear enough in the first case. I don't think I gave enough information.
 

Neverm1nd

Member
Jul 3, 2006
42
0
0
Neither of your types has constructors or destructors, so it's really quite simple, and doesn't work any differently than malloc and free.

When memory get allocated, your program gets an address followed by a chuck of memory. The size of this chunk is determined when you call new. Calling delete on a pointer frees that same chunk. You can cast it to anything you feel like, the delete operator will free the same block anyway.
 

Venix

Golden Member
Aug 22, 2002
1,084
3
81
Originally posted by: SunnyD
You guys don't seem to understand, or I didn't explain it correctly - either way... as per the previous sample:

struct TYPE2 {
...
};

struct TYPE1 {
int Count;
TYPE2 ArrayofTYPE2[1]; // Windows APIs use this alot...
};


Basically, you're declaring an instance of TYPE1 in your code. The problem is, TYPE2 is the "Variable" part of the structure in TYPE1. This is used extensively in Win32 because of the standards for C at the time Win32 was made - there was no way to declare a variable sized array.

The typical code would be something like:

TYPE1 *pType1 = (TYPE1*)malloc(sizeof(TYPE1) + (Multiple * sizeof(TYPE2)));
...
free(pType1);


Translating this to using new, you would obviously have to do the following in order to maintain a contiguous block of memory:

TYPE1 *pType1 = (TYPE1*) new unsigned char[sizeof(TYPE1) + (Multiple * sizeof(TYPE2))];

The question then becomes, how do you delete that pointer? I assume because it was created as an array of contiguous unsigned chars, you would want:

delete [] (unsigned char*)pType1;

I'm just not entirely certain of the behavior of the delete operator when it comes to this type of operation. Whereas free() would simply deallocate the entire allocated block as allocated by the memory manager, delete's constructs are designed to handle objects, which leads me to believe that I have to cast the pointer back to it's allocated original type in order to properly dispose of the memory.

Sorry if I wasn't clear enough in the first case. I don't think I gave enough information.

Er, that's exactly the question I answered: "you must call delete/delete[] with the same type as new/new[]".

So yes, if you insist on using manual memory management instead of smart pointers or other RAII structures, you must cast to unsigned char before calling delete.
 

Venix

Golden Member
Aug 22, 2002
1,084
3
81
Originally posted by: Neverm1nd
Neither of your types has constructors or destructors, so it's really quite simple, and doesn't work any differently than malloc and free.

When memory get allocated, your program gets an address followed by a chuck of memory. The size of this chunk is determined when you call new. Calling delete on a pointer frees that same chunk. You can cast it to anything you feel like, the delete operator will free the same block anyway.

This is true in practice, but not guaranteed by the C++ standard. Calling delete with a different type of pointer than what new returned is undefined behavior.
 

SunnyD

Belgian Waffler
Jan 2, 2001
32,674
146
106
www.neftastic.com
Originally posted by: Venix
Er, that's exactly the question I answered: "you must call delete/delete[] with the same type as new/new[]".

So yes, if you insist on using manual memory management instead of smart pointers or other RAII structures, you must cast to unsigned char before calling delete.

I don't insist... Win32 insists. I'm not the one that created the structures in the first place, otherwise I'd happily use something more... meaningful. These memory blocks are commonly read/written from Kernel-space.

 

Venix

Golden Member
Aug 22, 2002
1,084
3
81
Originally posted by: SunnyD
Originally posted by: Venix
Er, that's exactly the question I answered: "you must call delete/delete[] with the same type as new/new[]".

So yes, if you insist on using manual memory management instead of smart pointers or other RAII structures, you must cast to unsigned char before calling delete.

I don't insist... Win32 insists. I'm not the one that created the structures in the first place, otherwise I'd happily use something more... meaningful. These memory blocks are commonly read/written from Kernel-space.

I explained above how you can use a smart pointer or a vector. For example:

std::vector<unsigned char> buffer(size);
Gdiplus::ImageCodecInfo* codecs = reinterpret_cast<Gdiplus::ImageCodecInfo*>(&buffer[0]);

Gdiplus::GetImageEncoders(num, size, codecs);