5

I was reading about the delete and delete[] operators today and I can see that to free the memory allocated to myChar, I should call delete and to free memory for myCharArray I should use delete[].

However I have always thought of myChar as a pointer to an array of size 1. So why is it that we use delete in this case; and why do we need delete at all? Couldn't we have gotten away with using delete[] everywhere if myChar is effectively an array of size 1?

char* myChar = new char;
char* myCharArray = new char[5];

delete myChar;
delete[] myCharArray;
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
AdaRaider
  • 1,126
  • 9
  • 20
  • I actually did look at this page; my question is why is it necessary to have both delete and delete[]. – AdaRaider Aug 22 '18 at 08:22
  • For this case, it's just `delete(myCharArray)` – Susmit Agrawal Aug 22 '18 at 08:22
  • `delete[]` is necessary when objects are involved. `char` is a basic datatype. – Susmit Agrawal Aug 22 '18 at 08:23
  • 2
    Because every `new` should be accompanied by `delete` and every `new[]` should be accompanied by `delete[]`. In fact you should be using smart pointers instead. – Ron Aug 22 '18 at 08:23
  • Yes, you are absolutely right. What I am trying to understand is why we don't just call delete[] on pointers that effectively point to arrays of size 1? – AdaRaider Aug 22 '18 at 08:25
  • I can see that "using delete on a pointer returned by new [] results in undefined behavior." and I completely understand that but why don't we just have delete[] if that works for both myChar and myCharArray? – AdaRaider Aug 22 '18 at 08:29
  • 4
    Because objects are not arrays of size 1. If you define an array, the size of the array is stored somewhere in the memory (this is implementation defined). If you use `delete[]`, it has to know this size... But for plain pointers to objects, there is no such information... – Jaa-c Aug 22 '18 at 08:30
  • 2
    Just because it works here in this example, doesn't mean it will always work and you can rely on it, it's undefined behavior. Imagine you have a base class with virtual destructor and derived class, and you do `Base *ptr = new Derived`, if you use `delete[] ptr`, how is the compiler supposed to know which destructor to call? – Kaldrr Aug 22 '18 at 08:32
  • Ok, I think I see, so when I call delete[] myChar I don't get an exception thrown here, is this then an undefined operation on myChar? – AdaRaider Aug 22 '18 at 08:33
  • 1
    Also this is related: https://stackoverflow.com/questions/197675/how-does-delete-know-the-size-of-the-operand-array – Jaa-c Aug 22 '18 at 08:33
  • 2
    You won't get exception here, it's undefined behavior, not an operation that may throw exceptions. – Kaldrr Aug 22 '18 at 08:34
  • 2
    @AdaRaider Yes, it is undefined. If it appears to work, that's just bad luck. – molbdnilo Aug 22 '18 at 08:34
  • Ok I see now, thanks all. – AdaRaider Aug 22 '18 at 08:38
  • 3
    Reopened: this question isn't about the **difference** between `delete` and `delete[]`; it's about **why** they're different. – Pete Becker Aug 22 '18 at 12:24

2 Answers2

7

A single object is not an array of size 1. An array of size 1, created with new char[1], needs to record the number of objects that were allocated, so that delete[] knows how many objects to destroy.

Arguably, new could be implemented internally as new[1] (and delete implemented as delete[]) - that would be a correct implementation. However, having separate new and new[1] allows the optimization of not storing the object count for single objects (a very common case).

The C++ principle of not paying for what you don't use comes into play here. If we create an array of objects, we pay the memory and processing price of storing the count, but if we create a single object, we don't incur the overhead.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • A single object **is**, in may contexts, an array of size 1. `int i = 3; int* begin = &i; int* end = &(i + 1); while (begin != end) std::cout << *begin++;`. – Pete Becker Aug 22 '18 at 12:32
  • @PeteBecker i think it's rather effect of arrays decaying to pointers than objects being arrays of size 1. – el.pescado - нет войне Aug 22 '18 at 12:45
  • @TobySpeight -- "in the context of `delete[]` they are different" is just restating the question. – Pete Becker Aug 22 '18 at 12:59
  • @Pete, I've edited to make clearer that having distinct `new` and `new[]` (and their corresponding `delete` and `delete[]`) is an optimisation. Have I captured the essence better now? – Toby Speight Aug 22 '18 at 13:05
6

You're right that there's no inherent reason for the distinction between new/delete and new[]/delete[]; the language could have simply omitted the non-array version, or omitted delete and required delete[] to work correctly with a single object allocated by new. But doing that adds complexity and corresponding overhead to the implementation for the single-object case, since it would then have to store a size of 1 somewhere so that delete[] could do the right thing. One of the primary design principles of C++ was (and is) that you don't pay for what you don't use. new/delete is the result of this parsimony. It moves the cost from the implementation to the programmer.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165