I have a question about shared pointers, I found code on a site that is talking about shared pointers and circular reference. In the code below when albert is assigned jasmine in the last line, why does that work? Because jasmine has been assigned to point at albert at this point, so my thinking is currently jasmine is pointing at albert, then albert is being assigned to point at what jasmine is, so shouldnt albert end up pointing at albert too? or am I not thinking correctly about this?
No it hasn't. jasmine->companion is pointing to albert.
ah yeah i see how it is working now.
Can i ask another question, since it is basically the same code as above, dont want to make a new post since it is almost the same.
If I change the companion pointer to a weak pointer, shown below, how exactly does that work.
Does the object go out of scope, and the link between the jasmine pointer and its object (line 9) split, and then the same for the albert object and pointer. Then the compiler sees that the objects still exist and are pointed to by each companion pointer. So it then deletes each object ?
A weak_ptr is non-owning. It needs to take ownership of the object through a shared pointer with its lock() method before the object can be used. In the code you show, it's pretty much as if the weak pointers don't exist. The albert and jasmine objects are destroyed and the companion weak pointers don't even come into it.
Ganado has shown a more interesting example, that actually has a cycle and doesn't call the dtors because of that. If the companion is changed to a weak pointer then it works properly. Note that there is only one dtor call since only the shared pointer has ownership.
#include <iostream>
#include <memory>
struct Player
{
std::string name;
std::weak_ptr<Player> companion;
Player(std::string n) : name(n) { }
~Player() { std::cout << "~Player: " << name << '\n'; }
void say() const { std::cout << "I am " << name << '\n'; }
};
int main()
{
auto albert = std::make_shared<Player>("albert");
albert->companion = albert;
{
// We must get a shared pointer from the weak pointer before accessing the object.
auto sp = albert->companion.lock();
// And we need to test to see if we actually get a shared pointer
// since the object may have been destroyed by its owner(s).
if (sp)
sp->say();
}
// sp now out of scope, so there is only one owning shared pointer again
// and therefore no cycle.
}
Ok I understand about the weak pointer. I have spend the last day going through it all and trying different things to try and understand it fully using the below code.
I am wondering thought, why can i not do, albert->companion->say(); My thinking behind this is the albert pointer is pointing to the object that the companion pointer is a part of, and then that is pointing to the object that say is a method of, (which is believe is the same object in this case) Since this does not work, I am wondering if my thought process behind it is wrong or is there a reason that this does not work but my thought process is correct?
Really? You seem to be asking why you can't use the weak pointer directly without "locking" it to get a shared pointer first. That's pretty basic to understanding them.
The reason is that since the weak pointer doesn't "own" the object, the object may already have been deleted by the time you want to use it.
BTW, remember to prefer unique_ptr over shared_ptr when appropriate.
OK yeah re-reading what I was asking and thinking now sounds stupid. Sometimes when i spend hours going over something it all becomes a bit mixed up so I guess I was just a bit confused when thinking that.
That's a good question.
Remember that it's a question of ownership, which is basically the responsibility to delete the object.
So if it comes up naturally that there is an object that doesn't have a single definite owner, then you can use a shared_ptr.
Here's a contrived example. Maybe I'll think of a better one later.
#include <iostream>
#include <memory>
class B
{
public:
B() { std::cout << "B()\n"; }
~B() { std::cout << "~B()\n"; }
};
class A
{
std::shared_ptr<B> p;
public:
// A new A gets a new B
A() : p(new B) { std::cout << "A()\n"; }
// A copy of an A gets that A's B.
A(const A& o) : p(o.p) { std::cout << "A(copy)\n"; }
~A() { std::cout << "~A()\n"; }
};
int main()
{
A* a2;
{
A a1;
a2 = new A(a1);
} // a1 goes out of scope, but it's B lives on in a2
delete a2; // both a2 and its B are deleted
}