开发者

Difference between operator new() and operator new[]()?

Is there any difference between fncs: operator new and operator new[] (NOT new and new[] operators)? Except of course call syntax? I'm asking because I can allocate X number of bytes for my objs with ::operator new(sizeof(T)*numberOfObject) and then access them with array notation, so what's the big deal with ::operator new[]. Is it only syntactic sugar?

#include <new>
#include <iostream>
#include <malloc.h>

using namespace std;
struct X
{
  int data_;
  X(int v):data_(v){}
};
int _tmain(int argc, _TCHAR* argv[])
{
  unsigned no = 10;
  void* vp = ::operator new(sizeof(X) * no);
  cout << "Mem reserved: " << _msize(vp) << '\n';
  X* xp = static_cast<X*>(vp);
  for (unsigned i = 0; i < no; ++i)
  {
    new (xp + i) X(i);
  }
  for (unsigned i = 0开发者_JS百科; i < no; ++i)
  {
    cout << (xp[i]).data_ << '\n';
  }
  for (unsigned i = 0; i < no; ++i)
  {
    (xp + i)->~X();
  }
  ::operator delete(vp);
  return 0;
}


These functions (operator new etc.) are not generally intended to be called explicitly, but rather used implicitly by new/new[] expressions (symmetrically, operator delete/operator delete[] functions are invoked implicitly by delete/delete[] expressions). An expression that uses new syntax for non-array type will implicitly call operator new function, while an expression with new[] will implicitly call operator new[].

The important detail here is that an array created by new[] expression will normally be destroyed later by delete[] expression. The latter will need to know the number of objects to destruct (if the objects have non-trivial destructors), i.e. this information has to be passed somehow from new[] expression (when it was known) to the corresponding delete[] expression (when it is needed). In a typical implementation this information is stored inside the block allocated by new[] expression, which is why the memory size requested in the implicit call to operator new[] is normally greater than the product of the number of elements and the element size. The extra space is used to store the household information (number of elements, namely). Later delete[] expression will retrieve that household information and use it to invoke the correct number of destructors before actually freeing the memory by calling operator delete[].

In your example you are not using any of these mechanisms. In your example you are calling memory allocation functions explicitly, perform construction manually and completely ignore the destruction step (which is OK, since your object has trivial destructor), which means that at least for destruction purposes you don't need to track the exact number of elements in the array. In any case, you keep track of that number manually, in a no variable.

However, in general case it is not possible. In general case the code will use new[] expressions and delete[] expressions and the number of elements will have to get from new[] to delete[] somehow, meaning that it has to be stored internally, which is why there's a need for a dedicated memory allocation function for arrays - operator new[]. It is not equivalent to a mere operator new with the aforementioned product as size.


In your example code, you're using placement new to perform the construction that operator new[] performs automatically - with the difference that new[] will only perform default construction and you're performing a non-default placement construction.

The following is more or less equivalent to your example:

#include <iostream>

using namespace std;

struct X
{
    int data_;
    X(int v=0):data_(v){}
};

int main(int argc, char* argv[])
{
    unsigned no = 10;

    X* xp = new X[no];

    for (unsigned i = 0; i < no; ++i) {
        X tmp(i);
        xp[i] = tmp;
    }

    for (unsigned i = 0; i < no; ++i)
    {
        cout << (xp[i]).data_ << '\n';
    }

    delete[] xp;

    return 0;
}

The differences in this example are:

  • I believe the example here is more readable (casts can be ugly, and placement new is a pretty advanced technique that isn't often used, so isn't often understood)
  • it properly destroys the allocated objects (it doesn't matter in the example since the object are PODs, but in the general case, you need to call the dtor for each object in the array)
  • it has to default construct the array of objects, then iterate over them to set the actual value for the object - this is the one disadvantage in this example as opposed to yours

I think that in general, using new[]/delete[] is a much better than allocating raw memory and using placement new to construct the objects. It pushes the complexity of the bookkeeping into those operators instead of having it in your code. However, if the cost of the "default construct/set desired value" pair of operations is found to be too costly, then the complexity of doing it manually might be worthwhile. That should be a pretty rare situation.

Of course, any discussion of new[]/delete[] needs to mention that using new[]/'delete[]should probably be avoided in favor of usingstd::vector`.


There's not a whole lot of difference between the required behavior of the functions void* operator new(size_t) and void* operator new[](size_t), except that they're paired with different deallocation functions.

The operators themselves are very different. One of the differences between the operators is which allocation function is used, but there are ultimately many other differences including how many constructors get called, etc. But your example code isn't using the operators (well, it is using placement new). You might want to change your question title to be clear about that.

From section [basic.stc.dynamic.deallocation]:

If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_- t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.


Allocation is one thing, object construction/destruction is another.

A new performs one allocation and one construction. However, a new[] still allocates one continuous memory block, but calls many constructors.

The situation is the same with delete and delete[].

BTW- I'm not 100% sure with what I'm about to say, but I believe that you won't get an imemdiate memory leak if you call delete on an address received from new[]. The whole memory block would probably be freed. However, this is invalid because you'd call the destructor on the first object only, instead of on every object in the array. And this may result in secondary memory leaks... and lots of logic errors due to broken 1-1 relation of constructors and destructors, of course.

(Also, remember to consider using boost::array or std::vector instead of new[]! :))


As user sankoz explains in his answer to in fact the same question, having separate operator new[] is intended for overloading single object allocations and array allocations separately in classes.

For example, if you have some specific class and you know that its instances are never larger than say 50 bytes you might want to overload operator new for that class so that its instances are allocated on a superfast allocator for blocks of size 50.

But what if the user calls new[]? The array can have whatever number of elemenents, so you can't universally allocate them on your custom allocator. The solution is you don't have to care of array allocations unless you want to.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜