0

I'm reading C++ Primer Plus, 12.1.3, about memory, and some things about destructor really confuse me.

//Here is a default construtor of String class
String::String()
{
   len = 0;
   str = new char[1];
   str[0] = '\0';
}

and the book says, use str = new char[1] not str = new char, the two ways allocate same memory, but the 2nd is not compatible with destructor. Besides, the book says the below 3 ways is bad because they are not compatible with "delete"

char words[15] = "bad idea";
char *p1 = words;
char *p2 = new char;
char *p3;
delete [] p1; //undefined, so don't do it
delete [] p2; //undefined, so don't do it
delete [] p3; //undefined, so don't do it

I don't know what are the difference that make these 3 ways bad, can someone explain it to me? Thank you very much.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 5
    You `delete[]` what you `new[]`. You do not `delete[]` what you do not `new[]`. There isn't much to explain here. – n. m. could be an AI May 20 '21 at 13:54
  • 12
    You can only `delete` what you `new` and you must `delete[]` what you `new[]`. Also stop using `new` and `delete`; instead use `std::vector` and/or `std::make_unique` – Richard Critten May 20 '21 at 13:55
  • If the boo, suggests that you use `new`/`delete`, then it's best place is in the backyard. If that's where you have bins. – Enlico May 20 '21 at 13:58
  • 3
    @Enlico There's still a certain merit to understanding memory allocation semantics. Even if barely used in higher level modern applications. – Hatted Rooster May 20 '21 at 13:58
  • There are smart pointers with corresponding make functions, there are containers, there is `std::variant` and `std::optional` not to forget the move semantics (and RValue references). TL;DR: They did a lot of work to provide better alternatives and make the `new`/`new[]` at best obsolete for application development so that they vanish under the hood. `new` and `delete` have caused too much trouble in productive work... – Scheff's Cat May 20 '21 at 13:58
  • @HattedRooster To understand memory allocations, you first need to understand that `new` _expression_ (which is the form of `new` most people are familiar with) is not a memory allocation function. This is what `operator new` is for: http://eel.is/c++draft/basic.stc.dynamic. – Daniel Langr May 20 '21 at 14:05
  • @DanielLangr No I would say that's specifics. Bothering a beginner about the difference between the expression and the operator is a no go in my books. Sure once they become more familiar with the concept that'd be the next step, but I think it's a little soon. – Hatted Rooster May 20 '21 at 14:08
  • @HattedRooster, indeed I wrote _if the book suggesteds that you use_ (well, I tried to, but made a typo, ahahah). – Enlico May 20 '21 at 14:09
  • `new[]` allocates more memory to store the array length in order to correctly calls the destructor for all elements so you must use `delete[]` on it. See [Why does the `delete[]` syntax exist in C++?](https://stackoverflow.com/a/67586278/995714) – phuclv May 20 '21 at 14:12
  • @HattedRooster That kind-of depends what you mean by "_understanding allocations_". I sometimes meet coders who think `new` (expression) just allocates memory and don't bother about object construction. Explaining the difference between memory (storage) allocation and object construction might be, in my opinion, very useful even for beginners. – Daniel Langr May 20 '21 at 14:12
  • I strongly disagree with people that claim that we should simply forget the existence of `new`. This would just create a bunch of stupid programmers that have not even a scratch of an idea of what they write really does. This is very useful to learn it and how it works, to then better understand the usefulness of smart pointers or standard containers (or even write its own). Of course you can drive a car without knowing how it works internally, but you would be a much better driver if you know what is implied for each action you do. And... it would allow you to design/improve/repair a car. – Fareanor May 20 '21 at 14:13
  • @Fareanor: That's exactly why I don't let my wife drive my new Audi SQ7 Vorsprung. – Bathsheba May 20 '21 at 14:14
  • @Bathsheba Well... fair enough XD ahah – Fareanor May 20 '21 at 14:15
  • @Fareanor I guess it all depends on the level someone writes code. If you want just to use `std::string`, you don't need to care about `new`. If you want to implement `std::string`, then you likely will need it. – Daniel Langr May 20 '21 at 14:16
  • 2
    @DanielLangr Of course, but I would go even farther. (Very) often, people get trapped into iterator invalidations because they don't understand that `std::vector` have to reallocate memory when the maximum capacity is reached (simply because they don't even understand what **is** memory allocation). I'm convinced that knowing the mechanics behind anything is way better than just ignoring it (and worse, making it a rule of thumb to always ignore it is the best way to do stupid things at some point). – Fareanor May 20 '21 at 14:19

2 Answers2

8

new char[1] allocates a char array with a length of 1 while new char allocates a single char, these are fundamentally different.

In short, anytime you use new you would use delete to free the allocated memory. Anytime you use new[] you would use delete[] to free the allocated memory, that's just the rules.

Do note that while this is fine as a learning exercise nowadays there are more suitable alternatives. For example, for a dynamically allocated array you could use std::vector<char> instead of new char[] which has all the flexibility with none of the downsides such as manual memory management.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
3
char words[15] = "bad idea";
char *p1 = words;
char *p2 = new char;
char *p3;
delete [] p1; //undefined, so don't do it
delete [] p2; //undefined, so don't do it
delete [] p3; //undefined, so don't do it

As you mentioned the book…

Hopefully, it also explained why these three delete[]s are undefined.

If not, here we go:

char words[15] = "bad idea";
char *p1 = words;
delete[] p1; //undefined, so don't do it

p1 was not allocated with new char[] but got the address of an array allocated on stack. Hence, it may not be delete[]-ed.

char *p2 = new char;
delete [] p2; //undefined, so don't do it

p2 was allocated with new. delete p2 has to be used instead of delete[] p2;. This is Undefined Behavior but I heard rumors that certain compilers handled it in such a similar way under the hood that it even works (probably).

char *p3;
delete[] p3; //undefined, so don't do it

Oh, oh. p3 is an uninitialized pointer.
Calling delete[] p3 is disastrous…
as the program will pick a random address and apply its heap memory book-keeping on it. At best worst, your application will still work a bit further until it dies under mysterious conditions in something harmless like std::cout << std::endl; and you will have a hard time to find out where this comes from.

If the third case were

char *p3 = nullptr;
delete[] p3; // not undefined anymore

this would turn things into harmless.
delete (and delete[]) with a null pointer is a no-op and explicitly allowed.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • For instance, the difference between both cases at the assembly level is significant: https://godbolt.org/z/GYTh7f7Y7. They may still be compatible, but with `delete[]`, the number of array elements is read from `QWORD PTR [rdi-8]`. It is stored there by `new[]`, (`mov QWORD PTR [rax], 1`) but not by `new`. – Daniel Langr May 20 '21 at 14:32
  • @DanielLangr Have you tried this for the MSVC? (This is the compiler I heard the rumors about.) However, U.B. is U.B. - actually, not that important how undefined it is... – Scheff's Cat May 20 '21 at 14:48
  • It seems to be the same: https://godbolt.org/z/faEEqMWaW (`mov QWORD PTR [rax], 1` and `lea rbx, QWORD PTR [rcx-8]`). Anyway, I know it is UB, but sometimes I think it is useful to explain what can actually happen at the machine code level. Once the program is generated, it has some behavior defined by its machine code. Just the C++ standard does not care is such a case. – Daniel Langr May 20 '21 at 14:53