简体   繁体   中英

Why is ::operator new[] necessary when ::operator new is enough?

As we know, the C++ standard defines two forms of global allocation functions:

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

And also, the draft C++ standard (18.6.1.2 n3797) says:

227) It is not the direct responsibility of operator new or operator delete to note the repetition count or element size of the array. Those operations are performed elsewhere in the array new and delete expressions. The array new expression, may, however, increase the size argument to operator new to obtain space to store supplemental information.

What makes me confused is:

What if we remove void* operator new[](size_t); from the standard, and just use void* operator new(size_t) instead? What's the rationale to define a redundant global allocation function?

I think ::operator new[] may have been useful for fairly specialized systems where "big but few" arrays might be allocated by a different allocator than "small but numerous" objects. However, it's currently something of a relic.

operator new can reasonably expect that an object will be constructed at the exact address returned, but operator new[] cannot. The first bytes of the allocation block might be used for a size "cookie", the array might be sparsely initialized, etc. The distinction becomes more meaningful for member operator new , which may be specialized for its particular class.

In any case, ::operator new[] cannot be very essential, because std::vector (via std::allocator ), which is currently the most popular way to obtain dynamic arrays, ignores it.

In modern C++, custom allocators are generally a better choice than customized operator new . Actually, new expressions should be avoided entirely in favor of container (or smart-pointer, etc) classes, which provide more exception safety.

The standard (n3936) makes it clear that these two operators serve different but related purposes.

operator new calls the function void* operator new(std::size_t) . The first argument must be identical to the argument to the operator. It returns a block of storage suitably aligned, and which may be somewhat larger than required.

operator new[] calls the function void* operator new[](std::size_t) . The first argument may be larger than the argument supplied to the operator, to provide extra storage space if required for array indexing. The default implement for both is to simply call malloc().

The purpose of operator new[] is to support specialised array indexing, if available. It has nothing to do with memory pools or anything else. In a conforming implementation that made use of this feature, the implementation would set up specialised tables in the extra space and the compiler would generate code for instructions or calls to library library support routines that made use of those tables. C++ code using arrays and failing to use new[] would fail on those platforms.

I am not personally aware of any such implementation, but it resembles the kind of features required for the support of certain mainframes (CDC, IBM, etc) which have an architecture quite unlike the Intel or RISC chips we know and love.

In my opinion, the accepted answer is incorrect.


Just for completeness, the standard (n3936 mostly in S5.3.4) contains the following.

  1. A distinction between allocating an 'array object' or a 'non-array object'
  2. References to 'array allocation overhead', with the implication that extra storage might be needed and it might (somehow) be used for a repetition count or element size.

There is no reference to memory pools or any hint that this might be a consideration.

::operator new[] and ~ delete[] facilitate memory usage debugging, being a central point to audit allocation and deallocation operations; you can then ensure the array form is used for both or neither.

There are also lots of plausible if highly unusual/crude tuning uses:

  • allocate arrays from a separate pool, perhaps because that crucially improved average cache hits for small single-object dynamically-allocated objects,

  • different memory access hints (ala madvise ) for array/non-array data

All that's a bit weird and outside the day-to-day concerns of 99.999% of programmers, but why prevent it being possible?

I'm sure there are proper use-cases out there that require separate new[] and new , but I haven't encountered one yet that is uniquely possible with this separation and nothing else.

However, I see it like this: since the user calls different versions of operator new , the C++ standard would have been guilty of wantonly and deliberately losing information if they'd defined just one operator new and had both new and new[] forward there. There is (literally) one bit of information here, that might be useful to somebody , and I don't think people on the committee could have thrown it out in good conscience!

Besides, having to implement the extra new[] is a very very minor inconvenience to the rest of us, if at all, so the trade off of preserving a single bit of information wins against having to implement a single simple function in a small fraction of our programs.

The C++ Programming Language: Special Edition p 423 says

_The operator new() and operator delete() functions allow a user to take over allocation and deallocation of individual objects; operator new[]() and operator delete[]() serve exactly the same role for the allocation and deallocation of arrays.

Thanks Tony D for correcting my misunderstanding of this nuance.

Wow, it's not often I'm caught out on something in C++ I'm so certain about - I must have been spending too much time in Objective-C!

original wrong answer

It's simple - the new[] form invokes the constructor on every element of a classic C array.

So it first allocates the space for all the objects, then iterates calling the constructor for each slot.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM