2

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.

ЯegDwight
  • 24,821
  • 10
  • 45
  • 52
Simone Margaritelli
  • 4,584
  • 10
  • 45
  • 70

6 Answers6

9

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

  • I don't think so, i've just compiled it ... it's just a cast, what's the matter? – Simone Margaritelli Apr 13 '10 at 08:56
  • 1
    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 –  Apr 13 '10 at 09:00
  • 1
    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 Apr 13 '10 at 09:00
  • 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 Apr 13 '10 at 09:00
  • 2
    @Simone Becuase the behaviour is undefined, there is no telling what the compiled code might do. For your compiler it may seem to work. –  Apr 13 '10 at 09:03
  • @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 Apr 13 '10 at 09:05
  • @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 Apr 13 '10 at 09:07
  • This discussion reminds me of another question: http://stackoverflow.com/questions/2235457/how-to-explain-undefined-behavior-to-know-it-all-newbies – sharptooth Apr 13 '10 at 09:08
5

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
  • 54,544
  • 15
  • 116
  • 120
1

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
  • 8,023
  • 3
  • 32
  • 40
1

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
  • 167,383
  • 100
  • 513
  • 979
  • 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 Apr 13 '10 at 09:12
0

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
  • 181,030
  • 38
  • 327
  • 365
  • 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 Apr 13 '10 at 09:05
0

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 F
  • 42,307
  • 41
  • 144
  • 212