There are a couple of layers to the answers.
First, variables can be declared in several different ways:
- as local variables (either inside a function or as class members). Examples:
int i
, or int i = 42
. These have automatic storage duration (these examples are technically shorthand for auto int i
, or auto int i = 42
, although the auto
keyword is virtually never used. What this means is that these variables will be automaticaly freed when they go out of scope. Their destructor is guaranteed to be called (no matter how you leave the scope, whether it is by a function return or by throwing an exception, their destructor will be called when they go out of scope, and then the memory they use is freed). Local variables declared like this are allocated on the stack.
- as static variables (with the
static
keyword, implying static storage duration, as opposed to automatic shown above. These just stay around for the duration of the program, and so don't have to be freed
- on the heap, with dynamic allocation (
new int
or new int(42)
). These have to be freed manually by calling delete
.
So at the lowest level, you basically just have to preserve symmetry. If something was allocated with new
, free it with delete,
malloc is freed by
free, and
new[] by
delete[]`. And variables that are declared without any of these are automatically handled, and should not be manually freed.
Now, to keep memory management simple, RAII is typically used. The technique is based on the observation that only dynamically allocated objects have to be manually freed, and that local variables give you a very convenient hook for implementing custom actions when a local variable goes out of scope. So the dynamic allocation is wrapped up inside a separate class, which can be allocated as a local object on the stack. It can then, in its constructor, make any dynamic allocations you need, and its destructor cleans it up with the necessary delete
calls.
This means that you essentially never have to call delete
in your top-level code. It'll virtually always be hidden behind a RAII object's destructor. new
calls become rare too, but are still used together with smart pointers (such as this: boost::shared_ptr<int>(new int(42))
which dynamically allocates an integer, and then passes it to a smart pointer which takes over ownership of it, and cleans it up automatically.