In this video, we'll learn about the weak pointer.
The weak pointer provides a non-owning or weak reference to a shared manage object.
Like a shared pointer, a weak pointer points to an object on the heap.
But unlike a shared pointer, it doesn't participate in the owning relationship.
Weak pointers are always created from shared pointers.
And since weak pointers are non-owning references,
they do not affect the reference count for the managed object.
So weak pointers don't affect the lifetime of the objects they're pointing to.
There are a few use cases for the weak pointer,
one is to prevent strong reference cycles between two objects.
We'll see this visually in the next slide.
Another use case is when we have a pointer that we use to temporarily reference another object,
something like an iterator pointer that traverses a list of nodes.
It really doesn't own them, just visits them temporarily.
Let's see the problem with strong circular references.
Sometimes when we design programs we may have two classes
where each class refers to the other.
This implies a one-to-one relationship between the classes,
and this will be reflected in the class objects when we instantiate them.
In this case, we have two objects a and b,
which were instantiated from two different classes.
Notice that A has a shared pointer to B, and B has a shared pointer to A.
Now we have a bit of a chicken and egg problem.
When they go out of scope, they'll be destroyed from the stack,
but their shared resources on the heap will not be destroyed and will leak memory.
A keeps B alive, and B keeps A alive.
This is exactly where the weak pointer comes in. Let's see.
Now we need to decide who owns who. Let's say that A owns B.
Then we replace the shared pointer in B with a weak pointer.
And now everything works as expected, and we don't leak memory.
In the next video,
we'll complete this section with a brief introduction to custom deleters for smart pointers.
But first, let's head over to the IDE and see this example in live code.
Okay. So I'm back in the IDE. I'm in the section 17 workspace
in the weak pointers project.
And what we'll do here is we'll do that same example that we did visually in the slides, where I've got an A
referencing a B and a B referencing an A,
and we've got that strong circular reference that we can't break.
So let's take a look at how we do this.
First, of all you can see that I've got a class A
declared, right here on line 10 to 17.
And the class A has a shared pointer to a B.
Now since we haven't seen the B yet, we need to put this
forward declaration in here. I'm not sure if we've done that in class before or not.
But since the compiler needs to know about a B,
we're just simply saying, hey, you know what compiler, I've got a class B, I'll declare it later, just assume it's here.
It's similar to a function prototype in a sense that
just tells the compiler don't worry about it, you'll see this later.
So in this case, again, the class A has a shared pointer to a B.
And here's my set method that just sets that pointer.
It's got a real simple constructor and destructor that just simply display when they're called,
so we can see them actually being called.
And then my class B is defined down here.
And the class B has a shared pointer to an A.
So there's that relationship between the two, right.
So when A owns a B and a B onto A, so that's that's the problem that we have.
And here's my set method that sets that A pointer,
and again, my constructor. So really, really simple.
Let's take a look at the main.
And you can see the main here on line 31,
I create that A shared pointer using make shared.
And on line 32, I create the b
shared pointer using make shared.
Then right here I'm going to say
set the b part of a to b, and set the a part of b to a.
That's establishing that circular reference. So what happens here is,
here's my a, here's my b. Remember, these are shared pointers.
So we've basically got a pointer here and a pointer here.
Remember, it's a shared pointer. So this is just an object.
And this guy is pointing to an A object on the heap.
This guy's pointing to a B object on the heap.
And what we just did here is we did this.
He points to that one, and this guy points to that one.
Okay. So you can see the problem. Now this guy has a reference count of 1 or use count of 1.
This guy has a used count of 1. So what happens when a and b go out of scope.
Well, a goes out of scope, and it tries to destroy this piece here, right.
But it notices that something else is referencing it. So it's not 0.
So it's not going to destroy it, same thing with b.
So what ends up happening is what actually gets destroyed is a and b
that are on the stack, right,
the shared pointers, but not the area on the heap.
And there's your memory leak, right. So all this is going to leak.
And we've got no way to get to it.
So that's the problem. And the problem is because of this implied shared ownership.
So the fix is really simple. The fix is using a weak pointer.
So we determine who owns what.
Let's just assume that we're going to change this guy right here.
And actually, let me run this so you can actually see the leak.
Let me build and run.
And there's the run. You can see I constructed an, a I constructed a b,
but the destructors never got called because we leaked the memory.
That's what we want is -- we want a's constructor b's constructor.
Then we want obviously the destructors for both of them being called.
Okay. So let's do that. So what we can do is
you can see the comment right here, make it weak to break that strong circular reference.
And that's exactly what we're going to do. We're going to come right here,
we're going to make this shared pointer a weak pointer.
That won't affect the reference count.
So now when we run this, you can see the a constructor the b constructor then the
a and the b destructures being called.Now we're no longer leaking memory.
So this is a real, real common use case for the weak pointer.
And this kind of stuff happens more often than you would think --
when you've got very complex data structures and graphs and you're traversing and
doing all kinds of manipulation.
It's very possible you can end up with a strong circular references like these,
and the weak pointer is the solution.