shared pointers

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?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 struct Player
{
  std::shared_ptr<Player> companion;
  ~Player() { std::cout << "~Player\n"; }
};

int main()
{
  std::shared_ptr<Player> jasmine = std::make_shared<Player>();
  std::shared_ptr<Player> albert  = std::make_shared<Player>();

  jasmine->companion = albert; // (1)
  albert->companion  = jasmine; // (2)
}


this is the site if anyone wants to see, it is the section on shared pointers , the very last part of that section
https://www.internalpointers.com/post/beginner-s-look-smart-pointers-modern-c
Last edited on
Because jasmine has been assigned to point at albert at this point

No it hasn't. jasmine->companion is pointing to albert.
This is how you would make albert's data point to albert:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <memory>

struct Player
{
  std::shared_ptr<Player> companion;
  ~Player() { std::cout << "~Player\n"; }
};

int main()
{
    std::shared_ptr<Player> albert = std::make_shared<Player>();
    albert->companion = albert;
}

(The destructor won't be called in this case either)
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 ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 struct Player
{
std::weak_ptr<Player> companion;
  ~Player() { std::cout << "~Player\n"; }
};

int main()
{
  std::shared_ptr<Player> jasmine = std::make_shared<Player>();
  std::shared_ptr<Player> albert  = std::make_shared<Player>();

  jasmine->companion = albert; // (1)
  albert->companion  = jasmine; // (2)
}
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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.
}

Last edited on
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?



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

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");
    auto jas = std::make_shared<Player>("Jas");
    albert->companion = albert;
    jas->companion = jas;

    std::cout << albert.use_count() << '\n';
    std::cout << jas.use_count() << '\n';

    albert->say();
    jas->say();


    {
        auto sharedPointerAl = albert->companion.lock();
        auto sharedPointerJes = jas->companion.lock();
            

        if (sharedPointerAl)
            sharedPointerAl->say();

        if (sharedPointerJes)
            sharedPointerJes->say();

        std::cout << sharedPointerAl.use_count() << '\n';
        std::cout << sharedPointerJes.use_count() << '\n';


        albert->companion.reset();
        
        if (std::shared_ptr<Player> sharedPointerAl = albert->companion.lock()) // Weak pointer was reset, so now doesnt point to albert, but sharedPointer1 still exisits, so count still = 2
        {
            std::cout << "TEST";
        }
        
        auto sharedP = albert;

        std::cout << sharedPointerAl.use_count() << '\n';
        std::cout << albert.use_count() << '\n';
    }
   
}
Ok I understand about the weak pointer.

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    auto sp = make_shared<int>(42);
    auto sp2 = sp;

    cout << "sp:  " << *sp  << '\n';
    cout << "sp2: " << *sp2 << '\n';

    sp2.reset(); // reset sp2; doesn't delete object
                 // since sp still holds it
    if (sp2)
        cout << *sp2 << '\n';
    else
        cout << "sp2 no longer holds the object\n";

    weak_ptr<int> wp = sp;

    if (auto wp_sp = wp.lock())
        cout << "wp_sp: " << *wp_sp << '\n';
    else
        cout << "object was deleted\n";

    sp.reset(); // deletes object

    if (auto wp_sp = wp.lock())
        cout << "wp_sp: " << *wp_sp << '\n';
    else
        cout << "object was deleted\n";
}

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.
Interesting discussion! I am a newbie to C++ and was just reading up on shared pointers

https://www.nextptr.com/tutorial/ta1358374985/shared_ptr-basics-and-internals-with-examples#:~:text=shared_%20ptr%20-%20basics%20and%20internals%20with%20examples,handy%20yet%20straightforward%20utility.%20...%204%20References.%20

Are there good use cases for using shared pointers in real life?
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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
}

Topic archived. No new replies allowed.