Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top