-
Notifications
You must be signed in to change notification settings - Fork 0
Quickstart on Smart Pointers
To start things off, let's first introduce the three types of smart pointers
-
std::unique_ptr- Single owner, but may have multiple raw pointers (non-owning) -
std::shared_ptr- Multiple owners, with an option to get non-owning weak pointers -
std::weak_ptr- Non-owning pointer used with shared pointers
All three pointers work the exact same as a normal pointer (i.e. you can use x->param), with some key utility methods to access the underlying pointer:
-
get()- simple getter (DON'T DELETE THIS POINTER) -
release()- returns the pointer, but also clears the member variable on the smart pointer (DO DELETE THIS POINTER)
NOTE: release() is analogous to a getAndSet(null) in the COS226 world
For the most part, I suspect everyone will be dealing with unique pointers only, so I won't go into detail about how shared and weak pointers work together. Also, shared_ptr and weak_ptr are an easy "duct-tape" solution to using smart pointers, so don't be tempted to use them to get rid of compiler errors.
When using pointers, you should always be asking yourself the question "who is responsible for this memory?". As an example, in Practical 3 your Lair had a bunch of tiles, so it's only natural that the Lair manages the Tiles rather than something else.
A non-owning pointer is essentially a reference with extra steps; you know where the object exists in memory, but you are not responsible for cleaning it up. Smart pointers use these concepts in the following way:
- A smart pointer will take responsibility for creating/deleting an object/pointer (ownership)
- When you need the raw pointer but don't want to delete it, you can use the
getmethod (non-owning pointer) - If you need to take control of the raw pointer for your own memory management, you can use the
releasemethod (transfer of ownership)
NOTE: if you try to delete a non-owning pointer, the compiler will not complain, but you'll definitely get a segmentation fault in weird places, since a smart pointer tries to clean up data that's already been cleaned
The question now becomes: how do we enforce ownership? Some of lectures slides have hinted at this with phrases like "copy-assignable" or "move-assignable". These are examples of move semantics, where you can tell the compiler whether to copy the data (copy constructor), or simply move the data(move constructor). For simplicity, think of moving an object as changing who owns the object's resources, i.e. a transfer of ownership.
Consider the following example where we try to copy-assign a unique pointer:
// Create a new integer on the heap
std::unique_ptr<int> x = new std::unique_ptr<int>(new int);
// Try to copy x into y - ERROR
std::unique_ptr<int> y = x;The compiler will scream at you, because a std::unique_ptr makes sure that only one object owns the underlying pointer. But say we want to transfer the ownership from one place to another, e.g. when adding pointer to a vector:
using namespace std; // because I'm lazy to type out std:: all the time
vector<unique_ptr<MyObj>> vec;
// create and initialize a new object
unique_ptr<MyObj> obj(new MyObj);
obj->name = "Bob";
obj->id = 2;
...
// REEEEEE - you're trying to copy!!!
vec.push_back(obj);In this case we can use a special function called std::move: It tells the compiler that you are ready to transfer the ownership of MyObj from the function to the vector:
// happy happy haaaaapy
vec.push_back(std::move(obj));NOTE: this is a very simplified explanation of the intricacies that happen under the hood, so feel free to message me (Vincent) if you want more detail