views:

372

answers:

3

I've seen a class which is a class which is defined like this..

class StringChild : public StringBase
    {
public:
    //some non-virtual functions
    static StringChild* CreateMe(int size);
private:
    unsigned char iBuf[1];
    };

The static factory function has the following implementation..

return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();

So as far as I understand it this function is using placement new to extend this class.

Is this safe only because there is only 1 member and it's allocated on the heap?

+5  A: 

It's an old C trick that was used to work around the non-availablity of variable length arrays in plain C. Yes, it also works in C++ as long as you use suitable allocator constructs (like allocating a bunch of raw memory the desired size and then placement newing the object in there). It's safe as long as you don't wander over the end of the allocated memory, but it does tend to confuse at least some memory debuggers.

One thing you have to make absolutely certain when using this technique is that the variable length array is the last element in the object layout, otherwise you'll walk over other internal variables.

I am however a little dubious about the implementation of the factory function - I assume the 'size' parameter is actually the desired array size? Also, don't forget that you'd have to release the memory above using 'free' and not 'delete', even though the latter might work in most cases.

Unless there's a compelling reason as to why the memory has to be managed this way, I would simply replace the array with a std::vector.

Timo Geusch
I was under the assumption that you cannot garentee the order of the members, hense my question about is this only safe because there is only 1 member.
Dynite
AFAIK compilers don't mess with the memory layout (ie, the order of members is the order in which they're declared) but unfortunately I don't have an appropriate C++ reference to had to back this up. But to be compatible with C it would have to respect the declaration order.
Timo Geusch
As much as I've read on other questions, that is only the case for POD-types. For non-pod, as in this case, you cannot guarentee.
Dynite
Also Note: Before calling free() you must also manually call the destructor (NOT via delete). This is because you are using the placement new version of new.
Martin York
+3  A: 

This should be OK for PODs provided iBuf is the last member of the structure. The problems with non-PODs could be that eg. compiler is free to reorder public/private/protected members, virtual base classes end up at the end of the most derived object IIUC, etc.

Your structure is non-POD (it has a base class) so I wouldn't recommend it.

Also, if you create instances like this

return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();

You should make sure that memory acquired by malloc should be freed with free, so delete your instances like this:

obj->~StringChild();
free(obj);

Maybe you'd like to use ::operator new() for allocation

jpalecek
A: 

Strictly speaking, since StringChild is derived from StringBase it's not safe. The C++ standard does not specify the layout for base class subobjects. Clause 10 Paragraph 3:

The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified.

If StringChild were a POD struct, then such a technique would be safe.

Michael Burr