What is Dynamic Memory Allocation in C++?

2019-01-25 19:40发布

问题:

I'm learning about Dynamic Memory Allocation in C++ and the keywords new and new[] are mentioned. It is said to enable users to specify the size of the memory allocation at runtime, unlike simply declaring a variable or array with a fixed size in the source code.

I don't understand this concept. How does it work? I just need a clarification on the idea and an example would be helpful!

回答1:

So, if you want an array of 10 integers, you'd be writing:

int arr[10]; 

But what if you wanted to do something like this;

cout << "How many?";
cin >> num;

int arr[num];

Well, the C++ language doesn't allow that. Instead, you have to do:

int *arr = new int[num]; 

to create your array. And later on you MUST[1] use:

delete [] arr; 

to free the memory.

So, how does this work? When you call new, the C++ runtime library [the code that you didn't have to write that makes up the fundamentals of C++] will figure out how much space num integers take up, and find some space in memory for that. I'm not going into details of "how you find some memory". For now, just trust me, there is some memory available somewhere that can be used to store some integers in.

When you later call delete, that same memory is given back to the "pool" or "heap" of memory that it came from.

Of course, if you have a machine with, say, 256 MB of memory, and you try to ask for space to store 250 million integers, bearing in mind that an integer takes up more than one byte, it's not going to work out - there is no "magic" here - the memory is still limited to how much is available in the machine.... You just have the right to determine in the program, when it's running, how much memory you need, rather than having to decide when WRITING the program.

Edit: It is generally best to "hide" any memory allocation using the already existing "container-" and "wrapper-classes" that are useful for this very purpose. For example:

 std::vector<int> arr;

would work as a variable storage for integers, and you never have to worry about freeing the memory, or even knowing how many you need before you have stored them there.

 std::shared_ptr<int> arr = new int[num]; 

is another case, where when the "shared_ptr" is no longer in use [it track that inside the shared pointer class, so you never need to care about freeing the memory].

[1] If you don't want to leak memory, and it's "bad style" to leak memory. Not making anyone happy if you do.



回答2:

I've seen many posts about memory allocation in C++, questions about "new operator" vs "operator new", questions about new int(100) vs new int[100], questions about memory initialization... I think there should be an answer that summarizes everything clearly once and for all, and I'm choosing this question to write this summary. It is about dynamic memory allocation, ie allocations on the heap at runtime. I also provide a summary implementation (public domain).


C vs C++

Main functions for dynamic memory allocations:

  • In C (header <cstdlib>), we have mainly malloc and calloc and free. I won't talk about realloc.
  • in C++ (header <new>), we have:
    • Template single-object allocation with initialization arguments:
      • new T( args )
      • new (std::nothrow) T( args )
      • delete ( T* )
    • Template multiple-objects allocation with default initialization:
      • new T[ size_t ]
      • new (std::nothrow) T[ size_t ]
      • delete[] ( T* )
    • Template memory initialization without allocation for single or multiple objects:
      • new (void*) T( args )
      • new (void*) T[ size_t ]
    • Internal new-expressions for:
      • Raw memory allocation ::operator new( size_t );
      • Raw memory allocation without exception ::operator new( size_t, std::nothrow );
      • Raw memory initialization without allocation ::operator new( size_t, ptr ).

Please look at this post for a concise comparison.


Legacy C dynamic allocations

Main points: complete type-erasure (void* pointers), and therefore no construction/destruction, size specified in bytes (typically using sizeof).

malloc( size_t ) does not initialize memory at all (raw memory contains garbage, always initialize manually before use). calloc( size_t, size_t ) initializes all bits to 0 (slight overhead, but useful for POD numeric types). Any allocated memory should be released using free ONLY.

Construction/destruction of class instances should be done manually before use / before memory release.


C++ dynamic allocations

Main points: confusing because of similar syntaxes doing different things, all delete-statements call the destructor, all delete-statements take fully typed pointers, some new-statements return fully-typed pointers, some new-statements call some constructor.

Warning: as you will see below, new can either be a keyword OR function. It is best not to talk about "new operator" and/or "operator new" in order to avoid confusions. I call "new-statements" any valid statements that contain new either as a function or keyword. People also talk about "new-expressions", where new is the keyword and not the function.

Raw memory allocation (no initialization)

Do not use this yourself. This is used internally by new-expressions (see below).

  • ::operator new( size_t ) and ::operator new( size_t, std::nothrow ) take a size in bytes, and return a void* in case of success.
  • In case of failure, the former throws an exception std::bad_alloc, the latter returns NULL.
  • Use ::operator new( sizeof(T) ) for a single object of type T (and delete for release), and ::operator new( n*sizeof(T) ) for multiple objects (and delete[] for release).

These allocations do not initialize memory, and in particular, they do not call the default-constructor on the allocated objects. Therefore you MUST initialize ALL the elements manually before you release the allocation using either delete or delete[].

Note: I couldn't stress enough that you should NOT use this yourself. If you should use it, however, make sure you pass a pointer to void instead of a typed pointer when calling either delete or delete[] on such allocations (always after initializing manually). I have personally experienced runtime errors with non-POD types with some compilers (maybe my mistake).

Raw memory initialization (no allocation)

Do not use this yourself. This is used internally by new-expressions (see below). In the following, I assume void *ptr = ::operator new( n*sizeof(T) ) for some type T and size n.

Then ::operator new( n*sizeof(T), (T*) ptr ) initializes n elements of type T starting from ptr using the default constructor T::T(). There is no allocation here, only initialization using the default-constructor.

Single-object allocation & initialization

  • new T( args ) allocates and initializes memory for a single object of type T using the constructor T::T( args ). The default constructor will not be called unless arguments are omitted (ie new T() or even new T). Throws an exception std::bad_alloc on failure.
  • Same for new (std::nothrow) T( args ) except that it returns NULL in case of failure.
  • Use delete to call the destructor T::~T() and release the corresponding memory.

Multiple-objects allocation & initialization

  • new T[n] allocates and initializes memory for a n objects of type T using the default constructor. Throws an exception std::bad_alloc on failure.
  • Idem for new (std::nothrow) T[n] except that it returns NULL in case of failure.
  • Use delete[] to call the destructor T::~T() for each element and release the corresponding memory.

Memory initialization (aka "placement new")

No allocation here. Regardless of how the allocation was made:

  • new (ptr) T(args) calls the constructor T::T(args) on the memory stored at ptr. The default constructor is not called unless arguments are omitted.
  • new (ptr) T[n] calls the default constructor T::T() on n objects of type T stored from ptr to ptr+n (ie, n*sizeof(T) bytes).

Related posts

  • Concise comparison new/delete vs malloc/free
  • More verbose Malloc vs new, look at the answer of @Flexo
  • New operator vs operator new, avoid the confusion by not using these terms