0

Based on this question: Variable size type allocation

Will the following work?

{
    // size calculated.
    std::auto_ptr<Base> p(new((void*)(new char[size])) Packet());

    // Do Stuff
}

Where the Packet is POD struct where the last member is an array. The idea being to allow a dynamically sized array (like we used to do in C all those years ago)

struct Packet
{
    // STUFF
    int  data[1];
}
Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 2
    I have a feeling [this question](http://stackoverflow.com/q/5520591/500104) is pretty relevant. – Xeo Mar 15 '12 at 05:21
  • 1
    Sorry if I misunderstand something here. But are you basically asking whether it is ok to use `delete` (as opposed to `delete []`) to free memory that was allocated using `new []`? – jogojapan Mar 15 '12 at 06:44

2 Answers2

2

No, this does not work: objects constructed with any form of new taken extra parameters other than std::nothrow need to be destroyed explicitly and the memory been taken care of separately:

void* memory = operator new(size);
T* ptr = new(memory) T(args);
...
ptr->~T();
operator delete(memory);

Also note that the way to allocate raw memory is not something like new char[size]: this constructs char objects in the memory which need to be destroyed. I realize that neither construction nor destruction actually do anything on built-in types but I'm pretty an implementation is allowed to do something and there is no permission to skip these destructors as far as I know.

Finally, note that you also need to construct the int objects and the implementation is allowed to put something after the apparent end of the structure.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

I apologize if I miss an important point that is somehow implicit in the question and I don't see it. But, regarding this central line:

std::auto_ptr<Base> p(new((void*)(new char[size])) Packet());

This is what I think can be said about it:

  1. The constructor call at the end should be Packet, not Packet(), although in practice the compiler might accept it as is, and it might not make any difference

  2. The inner allocation new char[size] uses the array allocator new []. The CPP reference states about the expression new [array_n]:

Note that more than size_of( type ) * array_n might be allocated because of additional information encoded by the compiler (such as the size of the array, since this information is needed in order to destruct the objects in the array properly).

Now, the outer allocator call, new ((void*)(...)), is an instance of placement new, which is described here as follows:

void* operator new ( std::size_t, void* ptr ); does nothing, returns ptr.

In other words, it may happen that the call to new [] causes the compiler to allocate more memory than strictly required by the array and to encode size-related information in the extra space. However, since placement new does "nothing", it does not handle in any way or remove the extra information.

But, since the use of std::auto_ptr implies that that deallocation will be carried out using delete (and not delete []), the extra information will not be properly deallocated, hence a memory leak or worse may result.

Edit: To avoid relying on the CPP reference only, the relevant parts of the C++ Standard N3337 are as follows:

  • § 18.6.1.2 states that only delete shall be used to deallocate space allocated by new, and correspondingly delete [] for space allocated by new []
  • § 18.6.1.3 explicitly states that the placement forms of new and new [] do not perform any action. This implies that neither can be used to "transform" single-object space into array space.

Now perhaps the real question is whether the application of placement new proposed in the question would be valid if only delete [] was used to deallocate the space later. Perhaps the answer is undefined (which should be interpreted as equivalent to a "No").

jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • Actually `new Packet` and `new Packet()` are both valid (though have slightly different meanings (the second one will zero-initialize the structure the first will default-initialize the structure)) So its not wrong or bad. – Martin York Mar 15 '12 at 16:28
  • Please don't use cpprefeence as a source. It contains so many mistakes. Fee free to use it as a starting point but when you start quoting something as an authoritative source it should be the standard. Also the extra space added does not affect the memory returned as is not part of the memory that is returned (the standard has certain guarantees that are not violated the extra space is not part of this nor does it affect those guarantees). Get a copy of the standard here: http://stackoverflow.com/a/4653479/14065 – Martin York Mar 15 '12 at 16:33
  • @Loki Astari Of course I agree that the extra space allocated by `new []` is __not__ returned. But isn't that precisely the problem? That is why space allocated by `new []` must be deallocated using `delete []`, no? Because otherwise that space is not accounted for during deallocation. – jogojapan Mar 16 '12 at 01:43
  • @Loki Astari I have added references to the C++ Standard. – jogojapan Mar 16 '12 at 02:23