1

I can't find a complete example that shows how to eliminating strong circular references between shared_ptr.

Problem is how to use a weak_ptr to "close" the chain of generic elements and to access the "next" element with the weak_ptr.

Thank you.

EDIT:

For example, suppose to have Element e1, e2, e3; with pointer inside to the next element. In C we do

e1->Next = e2;
e2->Next = e3;
e3->Next = e1;

...and we could do e1->Next->Next->Next->Next->Next etc.

In C++ with shared_ptr we cannot do the last ->Next = e1 because of circular references and destructor will not release all the Element.

We need a weak_ptr : but what strategy to have the same result?

Marco Sanfilippo
  • 293
  • 3
  • 19
  • 3
    `shared_ptr` and `weak_ptr` are normally only used when the pointer *own* the underlying storage. In the case of a circular buffer, you typically have a head and tail pointer, but neither normally owns the underlying storage. For non-owning pointers like this, you raw pointers are normally fine. – Jerry Coffin Aug 29 '16 at 16:52
  • 3
    need more information. can you rephrase the question, with compilable code showing what you have tried? – Richard Hodges Aug 29 '16 at 16:53
  • 2
    Also, what does this have to do with "circular buffers"? Circular *references* aren't the same thing – Nicol Bolas Aug 29 '16 at 17:08
  • I think this was always a non-answerr to the problem of circular reference that somehow gained traction as an idea with `C++11`. The reference here: http://en.cppreference.com/w/cpp/memory/weak_ptr still says that a weak pointer is used to break circular references but it does not provide an example of how this would be done. – Galik Aug 29 '16 at 18:14
  • 1
    While a circular buffer is a chain of circular references, and hence a problem, it isn't just any chain of circular references: it is a circularly symmetric one. Without a symmetry break, no type-based solution can solve the issue (there is no "distinguished spot" to stick the weak pointer). You'll have to actually describe your entire problem (not just your attempted solution) to get a solution here. – Yakk - Adam Nevraumont Aug 29 '16 at 18:55
  • "In C we do `e1->setNext(e2);`" - That's not C. – Ami Tavory Sep 04 '16 at 09:58
  • @AmiTavory why not? `Element` can be a struct with a pointer to the next element. – Marco Sanfilippo Sep 05 '16 at 07:01
  • 1
    @MarcoSanfilippo C doesn't have methods, only free functions. – Ami Tavory Sep 05 '16 at 07:22
  • @Galik "_weak pointer is used to break circular references_" is pure horrible non design. Like throwing a toolbox at a screw and hoping the screw will do something. – curiousguy Dec 08 '17 at 21:18

2 Answers2

-1

Ok I found the solution.

When I need to access from the last to the first element of the chain (object, struct or typedef_data) simply use the weak_prt with lock() (maybe with expired() to check if poiter still exists...)

Example:

 std::weak_ptr<int> gw;

void f()
{
    if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    std::cout << *spt << "\n";
}
else {
        std::cout << "gw is expired\n";
    }
}

int main() {

{ //into the scope
    auto sp = std::make_shared<int>(42);
    gw = sp;

    f();
}
 // out of the scope
f();
}

So, output will be:

42
gw is expired

expired() is not used in this example but should be used before lock() and the return value of lock() MUST BE always checked.

Marco Sanfilippo
  • 293
  • 3
  • 19
-2

Probably this discussion can be useful: When is std::weak_ptr useful?. In particular, the third answer is what you are searching for.

The example above is about a tree where each node has shared_ptr to its children and a weak_ptr to its parent. If instead you want to make a sort of circular buffer through a concatenated list of nodes, then you have to make a normal list using strong references, then you close the loop adding a weak_ptr from the last node to the first. When, iterating over the list, you reach the weak_ptr and you want to go ahead, you can get a shared_ptr calling the lock method on the weak_ptr; note that lock doesn't destroy the weak_ptr.

Another way to get a shared_ptr from a weak_ptr is to construct a shared_ptr passing to the constructor the weak_ptr. The outcome is the same in case of success, while its much different if the weak_ptr is expired (lock return an empty shared_ptr, while the constructor throws and exception).

Community
  • 1
  • 1
Davide Visentin
  • 735
  • 5
  • 19
  • 3
    "*The example above is about a tree where each node has shared_ptr to its children and a weak_ptr to its parent.*" Why should it use a `weak_ptr` to its parent instead of a regular, non-owning pointer? Children don't own their parents in any way. Children rely on the existence of their parents, and child nodes should be destroyed *before* their parent nodes. So why should they hold a `weak_ptr`? – Nicol Bolas Aug 29 '16 at 19:49
  • The tree in the example is a particular type of tree that you want to navigate both top down and bottom up. The weak_ptr are used, instead of shared_ptr, because doing like this you can exploit the RAII tecnique, so that you can properly destroy all the tree by simply destroing the root. If you use shared_ptr, when you try to destroy the root, this will fail because off circular references. This is particularly important when an exception occurs and you exit the scope where the tree is defined before you can gracefully destroy it. P.S.: I'm not native English speaker, sorry for errors. – Davide Visentin Aug 29 '16 at 20:14
  • 2
    "*The weak_ptr are used, instead of shared_ptr*" I didn't say to use a `shared_ptr`; I said to use a "regular, *non-owning* pointer". That is, parents own their children; the children have *no ownership* of their parents. – Nicol Bolas Aug 29 '16 at 20:15
  • @DavideVisentin yes, that's what he means – Mooing Duck Jan 31 '17 at 21:24