views:

98

answers:

6

Let's say I have a first structure like this:

typedef struct {
    int  ivalue;
    char cvalue;
}
Foo;

And a second one:

typedef struct {
    int  ivalue;
    char cvalue;
    unsigned char some_data_block[0xFF];
}
Bar;

Now let's say I do the following:

Foo *pfoo;
Bar *pbar;

pbar = new Bar;

pfoo = (Foo *)pbar;

delete pfoo; 

Now, when I call the delete operator, how much memory does it free?

sizeof(int) + sizeof(char)  

Or

sizeof(int) + sizeof(char) + sizeof(char) * 0xFF

?

And if it's the first case due to the casting, is there any way to prevent this memory leak from happening?

Note: please don't answer "use C++ polymorphism" or similar, I am using this method for a reason.

+10  A: 

The amount of memory freed is undefined - your code is illegal in C++.

anon
I don't think so, i've just compiled it ... it's just a cast, what's the matter?
Simone Margaritelli
Just because your code compiles doesn't make it legal code.
anon
s/illegal/undefined behavior/; It is well-formed code, assuming these snippets are taken from the proper contexts. http://stackoverflow.com/questions/2046952/limit-the-confusion-caused-by-undefined-behavior/2047172#2047172
Roger Pate
Yeah... just like you can cast anything to a `void*`, and then cast a `void*` to anything (or even directly cast from one thing to another), doesn't mean you can actually do anything with anything else and have it work.
Amber
Ok, it compiles with no warnings (-Wall) and it works like a charm in a really bigger environment compared to that example ... maybe it's illegal but honestly ... i don't care, just wanted to know the memory thingy, that's all.
Simone Margaritelli
@Simone Becuase the behaviour is undefined, there is no telling what the compiled code might do. For your compiler it may seem to work.
anon
@Simone: just because something appears to "work like a charm", it doesn't mean that it's correct, bug free, or that it will always work. If you rely on undefined behaviour then you're just storing up trouble for a later date.
Paul R
@Neil ok, so let's say i receive a Foo * inside a function and i want to free it, probably it was "down casted" from another structure, how can i avoid this "undefined behavior" without changin the logic?
Simone Margaritelli
This discussion reminds me of another question: http://stackoverflow.com/questions/2235457/how-to-explain-undefined-behavior-to-know-it-all-newbies
sharptooth
+5  A: 

It will probably (there is no guarantee) work fine on your machine as with most implementations of delete (when freeing the memory) it doesn't matter what type the pointer is but the address. Due to that it will probably deallocate the memory allocated for Bar (in this case).

However, view delete as expanding to something like:

if ( ptr != 0 ){
    ptr->~ClassName();
    operator delete(ptr);
}

Consider the case where you pass the wrong type to delete. The wrong destructor gets called which won't result at an error at compile time but might cause an issue at run time.

Consider the case where the new and delete operators are overloaded...

Yacoby
+1  A: 

Don't worry, the amount of memory to be deleted is written inside of allocated memory block header, so your code will not leak anything and delete exactly what have been allocated. But the above will not be true if you will allocate it as array.

alemjerus
No, the OPs code contains undefined behavior. It can do whatever.
sharptooth
A: 

Aside from matters of language law, your code will, in practice, do the right thing and deallocate the entire object. Most compilers will just invoke the underlying malloc/free (or similar) when new/delete is invoked.

It will matter, however, if you have a non-trivial destructor in Bar. The lack of a virtual destructor means that only the base class destructor gets called when you delete through a base-class pointer.

IIRC, failure to call the derived destructor is the only caveat with the above code. Other than that, I believe it is valid code. (My C++ is rusty these days, so I could be way off the mark.)

Marcelo Cantos
no ctors neither dtors ... i'm "simulating" C++ polymorphism with C-style casting, but they are only structs being casted, no methods what so ever.
Simone Margaritelli
+1  A: 

The whole idea of such code is just undefined behavior. Don't do it. What happens if someone overloads operator new and operator delete for one struct and not for the other?

The only legal way to do what you want is to inherit both structs from the comon base with a virtual destructors - then you will have proper defined behavior and no problems with deallocation.

However if operator new and operator delete are not overloaded and both structs have trivial destructors it might just work allright on your implementation - the compiler will call ::operator delete() that has one parameter - the address of the block and it will free exactly the right amount of memory. However don't count on it - your code actually has undefined behavior.

sharptooth
Those structures are not intented to overload operators in the real project, and every [alloc/dealloc]ation follows strict rules i've imposed with macros ... it's not easy to explain in a comment, anyway, you answer seems like the best .. ty ^^
Simone Margaritelli
A: 

Agree with alemjerus - this doesn't create memory leak. My two cents: sizeof(int) + sizeof(char) may not be equal to sizeof(Foo) because of structure elements padding, so allocated/deallocated memory block size is sizeof(Foo).

Alex Farber