Option 1.
The easiest way is to avoid new
and delete
altogether.
Instead of:
b = new int;
b = new int;
delete b;
Write:
std::unique_ptr<int> b;
...
b = std::make_unique<int>();
b = std::make_unique<int>();
When b
is overwritten, or gets out of scope, it frees the memory. No delete
is required or even allowed. Note quite like the Java garbage collector, but good enough most of the time.
Option 2.
Another way is to use an add-on garbage collector, but that solution is not advisable. First, the garbage collector is conservative meaning that there is a probability to have a memory leak despite the garbage collector. Second, these solutions tend to make existing problems worse, since the memory leaks will not be noticed and treated when introduced. Treating memory leaks a year after introduction is orders of magnitude more difficult than to treat them an hour after introduction.
NOTE: Using unique_ptr
is not as safe as in garbage-collected languages such as C#,Java, and Python. It fails when there is no clear ownership model and when there is a circular ownership. If element a
has a unique_ptr
to b
and b
has a unique_ptr
to a
then they will never be freed. They will never be freed since a unique_ptr
frees the object in the destructor, but nobody will call the destructor of neither a
nor b
. If nobody calls the destructor, then the unique_ptr
will never delete the object, and never call the destructor of the other object.
NOTE 2: Sometimes std::shared_ptr
should be used instead of unique_ptr
, but that does not solve the issue of circular references. This only solves the issue of multiple-owners of a single object.
NOTE 3: These smart pointers do not lower the possibility of stack-overflow due to deep recursion in destructors. This is likely to happen in recursive destruction of long linked-lists or in unbalanced binary-trees. Using unique_ptr
for this cases simply hides the fact that recursion is taking place.