68

I stumbled upon a quiz that involved array declaration with different sizes. The first thing that came to my mind is that I would need to use dynamic allocation with the new command, like this:

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

However, I saw that one of the solutions allowed the following case:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

After a bit of research I read that g++ allows this, but it kept me thinking, in which cases is it then necessary to use dynamic allocation? Or is it that the compiler translates this as dynamic allocation?

The delete function is included. Note, however, that the question in here is not about memory leaks.

Boann
  • 44,932
  • 13
  • 106
  • 138
learning_dude
  • 950
  • 1
  • 3
  • 10
  • 54
    The second example uses a [variable-length array](https://en.wikipedia.org/wiki/Variable-length_array) which have never been a part of C++. For this case use `std::vector` instead (`std::vector array(N);`). – Some programmer dude Jan 20 '20 at 10:04
  • I am not quite sure, but I think this is no C++ standard (but provided from gcc). Also the size is limited – RoQuOTriX Jan 20 '20 at 10:04
  • 7
    The direct answer to your question should be: no, it is not getting deprecated. Even though modern versions of C++ provide many features simplifying memory ownership management (smart pointers), it is still common practice to allocate objects by invoking `new OBJ` directly. – pptaszni Jan 20 '20 at 10:10
  • The memory allocation has types: 1. On heap 2. On stack. Once you allocate it on stack it is not transferred to a heap area. – Build Succeeded Jan 20 '20 at 10:20
  • 9
    For other people who are confused about why people are talking about memory leaks, the question was edited to correct a bug that was not material to the question – Mike Caron Jan 20 '20 at 20:20
  • 4
    @Mannoj prefer to use the terms Dynamic and Automatic to heap and stack. It's rare but it's possible to implement C++ without heaps and stacks. – user4581301 Jan 21 '20 at 01:31
  • 1
    Nothing has ever been deprecated in C++ and nothing ever will. That's part of what C++ means. – JoelFan Jan 21 '20 at 17:55
  • 2
    I'm not sure if that's a joke or not. C++ has officially deprecated a few things but they do tend to live on unofficially for one reason or another. – user4581301 Jan 21 '20 at 20:37
  • The first example is terrible for efficiency. Use `std::vector` *outside* the loop and grow it inside the loop if necessary. (e.g. just using `.resize(N)` would not be terrible, although you could leave it larger to avoid re-running the constructor, i.e. zeroing elements when it shrinks and then grows). Either way this is going to be cheaper than using `new` inside the loop. – Peter Cordes Jan 21 '20 at 22:12
  • @PeterCordes thanks. Any comments about the second snippet? How would you compare it to your proposal? – learning_dude Jan 21 '20 at 22:27
  • If your compiler supports it, and `N` is guaranteed to be small enough, then it might be even more efficient. It avoids any stupid `std::vector` zeroing of data you were going to write before reading anyway, in case that matters. (Total stack size limits are for example 8MiB by default on x86-64 GNU/Linux in user-space, or like 16kiB in kernel space. Other platforms will have other constraints. Up to a few kiB is reasonable to alloc on the stack in most cases; if `N` might be larger than that, use dynamic allocation. new/delete don't support `std::realloc`, unfortunately...) – Peter Cordes Jan 21 '20 at 22:34
  • 1
    Using a VLA in a loop with a different size every iteration does compile to a few instructions to adjust + align the stack pointer every iteration, and to deallocate the VLA before the next iteration. https://godbolt.org/z/oMy6Rd shows GCC and clang for x86-64. It's branchless and only costs a few instructions. – Peter Cordes Jan 21 '20 at 22:36
  • 1
    @JoelFan In case that comment was meant to be taken seriously: Try a google search for `"removed in C++" site:en.cppreference.com` and look at the list of things that were *removed* from C++. Do the same with `"deprecated in C++" site:en.cppreference.com` and you will get even more things that were *deprecated*. – walnut Jan 22 '20 at 14:46

7 Answers7

117

Neither snippet you show is idiomatic, modern C++ code.

new and delete (and new[] and delete[]) are not deprecated in C++ and never will be. They are still the way to instantiate dynamically allocated objects. However, as you have to always match a new with a delete (and a new[] with a delete[]), they are best kept within (library) classes that ensure this for you. See Why should C++ programmers minimize use of 'new'?.

Your first snippet uses a "naked" new[] and then never delete[]s the created array. That's a problem. std::vector does everything you need here just fine. It will use some form of new behind the scenes (I won't dive into implementation details), but for all you have to care, it's a dynamic array but better and safer.

Your second snippet uses "variable length arrays" (VLAs), a C feature that some compilers also allow in C++ as an extension. Unlike new, VLAs are essentially allocated on the stack (a very limited resource). But more importantly, they are not a standard C++ feature and should be avoided because they are not portable. They certainly do not replace dynamic (i.e. heap) allocation.

Max Langhof
  • 22,398
  • 5
  • 38
  • 68
  • 3
    I would like to add that although VLAs are not in the standard officially, they are supported by all major compilers, and thus the decision of whether or not to avoid them is more of a matter of style/preference than a realistic concern for portability. – Stack Tracer Jan 21 '20 at 04:41
  • 4
    Also remember that you can't return an array or store it elsewhere, so VLA won't ever outlive the function's execution time – Ruslan Jan 21 '20 at 07:32
  • 16
    @StackTracer To the best of my knowledge, MSVC does not support VLAs. And MSVC is most definitely a "major compiler". – Max Langhof Jan 21 '20 at 08:44
  • 2
    *"you have to always match a new with a delete "* - not if you work with `Qt`, as its base classes all have garbage collectors, so you just use `new` and forget about it, most of the time. For GUI elements, when the parent widget is closed, the children go out of scope and are garbage collected automatically. – vsz Jan 21 '20 at 12:33
  • 2
    This answer would be even better if you included an example of idiomatic modern C++ code – Muzer Jan 21 '20 at 14:49
  • 6
    @vsz Even in Qt, each `new` still has a matching `delete`; it's just that the `delete`s are done by the parent widget rather than in the same code block as the `new`s. – jjramsey Jan 21 '20 at 17:14
  • Regarding VLA: They are such a fantastic way to introduce bugs into programs (One stop shopping for all your stack overflow needs and throws a couple significant monkey wrenches into `sizeof`) that I consider the reduced portability to be a lesser concern. – user4581301 Jan 21 '20 at 20:32
  • @jjramsey : my point was that the programmer doesn't have to write `delete`, because they are all in libraries / autogenerated code / wherever. – vsz Jan 22 '20 at 05:22
  • I'm not sure why you refer to the stack as a limited resource (presumably versus heap). As I was taught in the 80s, stack and heap grow into the same space from opposite directions. Obviously there are other implementations. But, for example, my current target has stack and no heap (obviously not c++, I admit). But in any case, there is no universal that stack is less available than heap. – Mike Layton Jan 23 '20 at 21:16
  • Stack area is reserved ahead of time, with a size provided by executable headers (or as a parameter to thread create) – Simon Buchan Jan 24 '20 at 03:39
  • ... The default may only be 1MB, so you don't want to waste it! It can even be slower than the heap, due to the use of guard pages to implement commit on use – Simon Buchan Jan 24 '20 at 03:46
  • @MikeLayton Stack *used to be simple*. But that was way back in the day of static executables and no ASLR. Now your stack is likely to be limited by the first thing it runs into in the virtual memory space. If that's on a 32-bit system it could be within a few megabytes. And that isn't counting thread stacks, which are not allowed to grow. – Zan Lynx Jan 24 '20 at 19:23
  • As I said, "there are other implementations." My point was that stack *may* be every bit as unlimited as heap. And whether that means virtually infinite or non-existent will depend on your settings, compiler, and target. Chances are, if memory is tight, it will be more optimal to have stack and no heap, because you must have stack, but you can leave out the memory management that way. – Mike Layton Feb 19 '20 at 21:37
24

Well, for starters, new/delete are not getting deprecated.

In your specific case, they're not the only solution, though. What you pick depends on what got hidden under your "do something with array" comment.

Your 2nd example uses a non-standard VLA extension which tries to fit the array on the stack. This has certain limitations - namely limited size and the inability to use this memory after the array goes out of scope. You can't move it out, it will "disappear" after the stack unwinds.

So if your only goal is to do a local computation and then throw the data away, it might actually work fine. However, a more robust approach would be to allocate the memory dynamically, preferrably with std::vector. That way you get the ability to create space for exactly as many elements as you need basing on a runtime value (which is what we're going for all along), but it will also clean itself up nicely, and you can move it out of this scope if you want to keep the memory in use for later.

Circling back to the beginning, vector will probably use new a few layers deeper, but you shouldn't be concerned with that, as the interface it presents is much superior. In that sense, using new and delete can be considered discouraged.

Bartek Banachewicz
  • 36,624
  • 6
  • 81
  • 129
  • 2
    Note the "...few layers deeper". If you were to implement your own containers, you should still avoid using `new` and `delete`, but rather use smart pointers like `std::unique_pointer`. – Max Jan 20 '20 at 10:45
  • 3
    @Max : `std::unique_ptr`'s default destructor calls `delete` or `delete[]`, which means that the owned object must have been allocated by `new` or `new[]` anyway, which calls have been hidden into `std::make_unique` since C++14. – Laurent LA RIZZA Jan 21 '20 at 11:48
  • Except, of course, that's the behavior of the **default** deleter. You can create a `unique_ptr`-based type with something other, and my goto FILE* handler for some time now is an incarnation of `struct fcloser { void operator()(FILE* file) { std::fclose(file); }};` `auto open(char const* path, char const* mode = "rb") {` `return std::unique_ptr{std::fopen(path, mode)};` `}` – Marcin Zdun Jan 25 '21 at 08:14
16

Your second examples uses variable length arrays (VLAs), which are actually a C99 (not C++!) feature, but nonetheless supported by g++.

See also this answer.

Note that variable length arrays are different from new/delete and do not "deprecate" them in any way.

Be also aware that VLAs are not ISO C++.

andreee
  • 3,626
  • 16
  • 33
13

Modern C++ provides easier ways to work with dynamic allocations. Smart pointers can take care about the cleanup after exceptions (that may happen anywhere if allowed) and early returns, as soon as the referenced data structures go out of scope, so may make sense to use these instead:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

From C++ 14 you can also write

auto buffer_new = std::make_unique<int[]>(size);

this looks even nicer and would prevent memory leak if the allocation fails. From C++ 20 you should be able to do as much as

auto a = std::make_shared<int[]>(size);

this for me still does not compile at the time of writing with gcc 7.4.0. In these two examples we also use auto instead of type declaration on the left. In all cases, use array as usual:

buffer_old[0] = buffer_new[0] = 17;

Memory leaks from new and crashes from doubled delete is something C++ has been bashed for many years, being the "central point" of argumentation for switching into other languages. Maybe better to avoid.

Audrius Meskauskas
  • 18,378
  • 9
  • 63
  • 78
  • You should avoid the `unique/shared_ptr` constructors in favor of `make_unique/shared`, not only do you not have to write the constructed type twice (using `auto`) but you don't risk leaking memory or resources if construction fails partway (if you're using a type that can fail) – Simon Buchan Jan 24 '20 at 03:59
  • 2
    make_unique is available with arrays from C++14, and make_shared from C++20 only. This is still seldom a default setting so proposing std::make_shared(size) looked for me somewhat ahead of time. – Audrius Meskauskas Jan 24 '20 at 07:48
  • Fair enough! I don't really use `make_shared` much, when you pretty much always want `vector`, but good to know. – Simon Buchan Feb 04 '20 at 08:12
  • Excessive pedantry, but IIRC the `unique_ptr` constructor is nothrow, so for `T` that have nothrow constructors, so there's no risk of leaks with `unique_ptr(new int[size])` and `shared_ptr` has the following: "If an exception is thrown, delete p is called when T is not an array type, delete[] p otherwise.", so you have the same effect - the risk is for `unique/shared_ptr(new MyPossiblyAllocatingType[size])`. – Simon Buchan Feb 04 '20 at 08:29
3

new and delete are not getting deprecated.

The objects created by new operator can be passed by reference. The objects can be deleted using delete.

new and delete are the foundational aspects of the language. Persistence of an object can be managed using new and delete. These are definitely not going to be deprecated.

The statement - int array[N] is a way of defining an array. The array can be used within the scope of the enclosing code block. It cannot be passed like how an object is passed to another function.

Gopinath
  • 3,185
  • 1
  • 8
  • 14
2

The first example needs a delete[] at the end, or you will have a memory leak.

The second example use variable array length that is not supported by C++; it only allows constant-expression for array length.

In this case it is useful to use std::vector<> as solution; that wraps all the actions you can perform on an array into a template class.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
Zig Razor
  • 2,907
  • 2
  • 8
  • 27
  • 3
    What do you mean with "until C++11"? I'm pretty sure, VLAs never became part of the standard. – churill Jan 20 '20 at 10:17
  • look at standard of c++14 [ c++14 standard ](https://isocpp.org/files/papers/N3690.pdf) on page 184 paragraph 8.3.4 – Zig Razor Jan 20 '20 at 10:23
  • 4
    Thats not _the_ standard, but only a draft and the part about “arrays of runtime bound" did not make it into the standard as far as I can tell. [cppreference](https://en.cppreference.com/w/cpp/language/array) does not mention VLAs there. – churill Jan 20 '20 at 10:37
  • Ah ok, i thought that is the standard not only the draft – Zig Razor Jan 20 '20 at 10:40
  • 1
    @zigrazor cppreference.com has [a list](https://en.cppreference.com/w/cpp/links) of links to the closest drafts before/after the publication of each of the standards. The published standards are not freely available, but these drafts should be very close. As you can see from the document numbers, your linked draft is some older working draft for C++14. – walnut Jan 20 '20 at 10:54
  • As you can see in the snippet, the size is not a constant expression and it is supported. I think is a good idea you edit your answer and specify that it is supported by standards. The answer could be misleading – learning_dude Jan 21 '20 at 07:13
  • 2
    @learning_dude It is *not* supported by standards. The answer is (now) correct (albeit short). It only works for you because GCC allows it as a *non-standard* extension. – walnut Jan 21 '20 at 07:53
-4

The syntax looks like C++, but the idiom is similar to plain old Algol60. It was common to have code blocks like this:

read n;
begin
    integer array x[1:n];
    ... 
end;

The example could be written as:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

I sometimes miss this in the current languages ;)

kdo
  • 266
  • 1
  • 3