views:

282

answers:

5

Looks like operator new and operator new[] have exactly the same signature:

void* operator new( size_t size );
void* operator new[]( size_t size );

and do exactly the same: either return a pointer to a big enough block of raw (not initialized in any way) memory or throw an exception.

Also operator new is called internally when I create an object with new and operator new[] - when I create an array of objects with new[]. Still the above two special functions are called by C++ internally in exactly the same manner and I don't see how the two calls can have different meanings.

What's the purpose of having two different functions with exactly the same signatures and exactly the same behavior?

+7  A: 

The operators can be overridden (for a specific class, or within a namespace, or globally), and this allows you to provide separate versions if you want to treat object allocations differently from array allocations. For example, you might want to allocate from different memory pools.

Mike Seymour
+7  A: 

I've had a reasonably good look at this, and to be blunt there's no reason from an interface standpoint.

The only possible reason that I can think of is to allow an optimization hint for the implementation, operator new[] is likely to be called upon to allocate larger blocks of memory; but that is a really, really tenuous supposition as you could new a very large structure or new char[2] which doesn't really count as large.

Note that operator new[] doesn't add any magic extra storage for the array count or anything. It is the job of the new[] operator to work out how much overhead (if any) is needed and to pass the correct byte count to operator new[].

[A test with gcc indicates that no extra storage is needed by new[] unless the type of the array members being constructed have a non-trivial desctructor.]

From an interface and contract standpoint (other than require the use of the correct corresponding deallocation function) operator new and operator new[] are identical.

Charles Bailey
The storage is only needed if the delete[] operator has to call dtors in a loop. (Which you almost said, but explicitly for anyone who reads this and wonders.)
Roger Pate
@Roger Pate: Yes, thank you. I'm good at 'almost' saying things. Of course, how it actually knows how many destructors need to be called is strictly an implementation detail but storing it with the dynamically allocated array is the most obvious implementation.
Charles Bailey
+5  A: 

One purpose is that they can be separately defined by the user. So if I want to initialize memory in single heap-allocated objects to 0xFEFEFEFE and memory in heap-allocated arrays to 0xEFEFEFEF, because I think it will help me with debugging, then I can.

Whether that's worth it is another matter. I guess if your particular program mostly uses quite small objects, and quite large arrays, then you could allocate off different heaps in the hope that this will reduce fragmentation. But equally you could identify the classes which you allocate large arrays of, and just override operator new[] for those classes. Or operator new could switch between different heaps based on the size.

There is actually a difference in the wording of the requirements. One allocates memory aligned for any object of the specified size, the other allocates memory aligned for any array of the specified size. I don't think there's any difference - an array of size 1 surely has the same alignment as an object - but I could be mistaken. The fact that by default the array version returns the same as the object version strongly suggests there is no difference. Or at least that the alignment requirements on an object are stricter than those on an array, which I can't make any sense of...

Steve Jessop
...especially as an object of array type is still an object.
Charles Bailey
Yes, I was just thinking of editing to add that. But an object of array type isn't *any* object, it's part of a strict subset of all objects, and by that token perhaps could have weaker alignment requirements. I'm not sure if it's legal for `sizeof (T[1]) > sizeof (T)`, but if it is then you can imagine that on some implementation the alignment requirement for an *array* of size 8 bytes might only be 4 (since that's the biggest T can be), whereas the alignment requirement for an *object* of size 8 bytes might be 8 (because there exists an 8-aligned type).
Steve Jessop
I'm fairly sure that it isn't allowed that `sizeof(T[1]) > sizeof(T)` for reason that I can't remember; something todo with the definition of pointer arithmetic, perhaps. The alignment of `T[1]` could be > sizeof `T[1]`, though. I think it is legal to _placement-new_ and array into memory allocated by `operator new` precisely because an array is an object.
Charles Bailey
Oh yes, the whole array size caculation `sizeof array / sizeof array[0]` would be totally wrong if `sizeof(T[1]) != sizeof(T)` and I'm fairly sure that that common array size calculation is well defined.
Charles Bailey
Good point. As for placement new, I don't think I've ever used it with arrays (or for that matter at all, in "real code"). How do you delete an array created with placement new? Somebody must be remembering the size, so either it's the application, or else you can't placement-new a `MyClass[2]` into `2*sizeof(MyClass)` bytes of storage.
Steve Jessop
Charles Bailey
"I became bored before I found the parts that actually imply it" - don't blame you, I'm willing to take the standard's word for it if it draws conclusions from its own text. Padding within arrays yes, would not be contiguous, but the sizeof equality also rules out padding before and (especially) after the elements. Before would mean that the first element isn't at the same location as the array, which I suspect probably is banned somewhere although I haven't looked. After, I'm not so sure is explicitly banned. Btw, `-)
Steve Jessop
Err, yes, I obviously meant to cast to `char*` for the whole argument about `sizeof` to work. Then I would have been out of comment space, though. :-)
Charles Bailey
@Charles: "The sizeof operator yields the number of bytes in the object representation of its operand." [5.3.3/1] "An object of array type contains a contiguously allocated non-empty set of N sub-objects of type T." [8.3.5/1] "When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of *n* elements is *n* times the size of an element." [5.3.3/2] Best support I can find for it.
Roger Pate
A: 

Standard says that new T calls operator new( ) and new T[ ] results in a call of operator new[]( ). You could overload them if you want. I believe that there is no difference between them by default. Standard says that they are replaceable (3.7.3/2):

The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (18.4.1). A C + + program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (17.4.3.4). The following allocation and deallocation functions (18.4) are implicitly declared in global scope in each translation unit of a program

void* operator new(std::size_t) throw(std::bad_alloc);  
void* operator new[](std::size_t) throw(std::bad_alloc);  
void operator delete(void*) throw();  
void operator delete[](void*) throw();  
Kirill V. Lyadvinsky
+5  A: 

In Design and Evolution of C++ (section 10.3), Stroustrup mentions that if the new operator for object X was itself used for allocating an array of object X, then the writer of X::operator new() would have to deal with array allocation too, which is not the common usage for new() and add complexity. So, it was not considered to use new() for array allocation. Then, there was no easy way to allocate different storage areas for dynamic arrays. The solution was to provide separate allocator and deallocator methods for arrays: new[] and delete[].

sankoz
This is the real answer. The others are speculation on why it is that way based the ramifications now that we do have them separate.
Roger Pate