fbpx

The C++ language guarantees that destructors run at specific times even if an exception is thrown. You can use this to make sure that other cleanup work gets done. That’s what smart pointers do.

Let’s say you create a new object by calling new. You’re going to get a pointer to the new object that lives somewhere in the main heap memory. This is not a local variable on the stack. Even though you might have a local pointer variable that is on the stack, it’s where that pointer points to that’s important. When you’re done using the newly created object instance, you need to delete it.

What if you forget to delete it? Or what if you have code to delete it but somebody modifies the code later and adds a new place where the current method can return without deleting the object? Or what if an exception is thrown that completely jumps out of the method into some other method higher in the call stack where there’s a catch block?

In all these cases, the pointer goes out of scope and gets cleaned up from the method call stack. But the newly created object lives on in main memory. It’s now completely forgotten about by the application. This is a memory leak. One or two of these in a normal application as long as they aren’t too big will probably go unnoticed. The operating system will make sure to reclaim the memory when the application ends.

But if this is a game that needs to squeeze as much performance as possible from the computer and that some user will play for hours on hours, then a few small memory leaks over time can add up and affect the game. And if this is a service application running on a server and designed to run for weeks or months at a time, then a small memory leak can eventually bring the whole server to a stop.

The C++ language gets a lot of bad reputation for things like this. And it’s strange because it’s so easy to prevent. All you have to do is stop using raw pointers and start using smart pointers. Your code will for the most part not even know the difference. Your application is protected from accidental forgetfulness and from errors that cause your application to jump to another place.

This episode explains more about how smart pointers work. Listen to the full episode or you can also read the full transcript below.

Transcript

Episode 16 explains scope and episode 19 explains C++ destructors. When you put these two concepts together, then you know exactly when your destructors will be run. You can put code in your destructors to do interesting things.

Let’s say you create a new object by calling new. You’re going to get a pointer to the new object that lives somewhere in the main heap memory. This is not a local variable on the stack. Even though you might have a local pointer variable that is on the stack, it’s where that pointer points to that’s important. When you’re done using the newly created object instance, you need to delete it.

What if you forget to delete it? Or what if you have code to delete it but somebody modifies the code later and adds a new place where the current method can return without deleting the object? Or what if an exception is thrown that completely jumps out of the method into some other method higher in the call stack where there’s a catch block?

In all these cases, the pointer goes out of scope and gets cleaned up from the method call stack. But the newly created object lives on in main memory. It’s now completely forgotten about by the application. This is a memory leak. One or two of these in a normal application as long as they aren’t too big will probably go unnoticed. The operating system will make sure to reclaim the memory when the application ends.

But if this is a game that needs to squeeze as much performance as possible from the computer and that some user will play for hours on hours, then a few small memory leaks over time can add up and affect the game. And if this is a service application running on a server and designed to run for weeks or months at a time, then a small memory leak can eventually bring the whole server to a stop.

The C++ language gets a lot of bad reputation for things like this. And it’s strange because it’s so easy to prevent. All you have to do is stop using raw pointers and start using smart pointers. Your code will for the most part not even know the difference. Your application is protected from accidental forgetfulness and from errors that cause your application to jump to another place.

There are different kinds of smart pointers and I’ll explain what they are and how to use them right after this message from our sponsor.

There are two requirements for a smart pointer. The first is that it should wrap up and manage a raw pointer so that it appears to have the same behavior as the raw pointer. It’s like it impersonates a pointer. And the second is that it has code in its destructor that will delete the raw pointer.

You can actually write similar classes yourself to perform any kind of cleanup needed. Maybe you don’t need to delete anything but just want to guarantee that some code will be run. Some methods are meant to be called in pairs. Like new and delete, you’ll come across methods like open and close, begin and end, start and stop, increment and decrement, subscribe and unsubscribe, etc. Each of these starts out by calling the first method and then you need to make sure to call the second method when your done. Well, the same idea that enables smart pointers to always call delete can be used to make sure to call whatever second method you want to call.

You need to make sure that when you’re working with smart pointers that you really are working with smart pointer objects. Remember they’re designed to impersonate a pointer. But they’re not really pointer themselves. This means that if you have a pointer to a smart pointer, then you just defeated the whole purpose of using the smart pointer. Make sure to use the smart pointers directly. It’ll look like you’re using a pointer but you’re not and that’s by design.

How is a smart pointer able to impersonate a real pointer? The same way any class can impersonate another. You write methods in the impersonating class that mimic those of the real class and then you can do whatever you want in your new methods including calling into the real methods.

A raw pointer really has just two methods we’re interested in and they’re both operators. It’s a good thing C++ lets you override or provide your own implementation of operators or you would never be able to fully impersonate a real pointer.

The two methods you need are the dereference operator and the dereference pointer operator. They look like an asterisk and an arrow. Although, normally I just call them a star and an arrow.

The star dereferencing operator returns a reference to whatever object the smart pointer points to. This allows callers to access the object directly. And the arrow dereferencing operator returns a pointer to the object. This might seem like the more roundabout way but it’s actually easier for callers to access the object through the arrow operator. So callers are more likely to need the arrow dereferencing operator more.

For each of these, you’ll want to provide versions that work with const pointers and versions for non-const pointers. This gives the widest range of use.

You’ll also want to overload both types. Don’t think that since callers are more likely to use the arrow dereferencing operators that’s all you need to provide. If you have one of them, then you should have both.