views:

783

answers:

8

shared_ptr is a reference counting smart pointer in the Boost library.

The problem with reference counting is that it cannot dispose of cycles. I am wondering how one would go about solving this in C++.

Please no suggestions like: "don't make cycles", or "use a weak_ptr".

Edit

I don't like suggestions that say to just use a weak_ptr because obviously if you know you will create a cycle, then you wouldn't have a problem. You also cannot know you will have a cycle in compile time if you generate shared_ptrs during runtime.

So please, self delete answers that use weak_ptr in them because I specifically asked not to have those kind of answers...

+3  A: 

It's fairly easy to detect cycles:

  • set a count to some largish number, say 1000 (exact size depends on your application)
  • start with the pionter you are interested in and follow pointers from it
  • for each pointer you follow, decrement the count
  • if the count drops to zero before you reach the end of the pointer chain, you have a cycle

It's not, however, very useful. And it is not generally possible to solve the cycvle problem for ref-counted pointers - that's why alternative garbage colection schemes like generation scavenging were invented.

anon
+1  A: 

I haven't found a much better method than drawing large UML graphs and looking out for cycles.

To debug, I use an instance counter going to the registry, like this:

template <DWORD id>
class CDbgInstCount
{
public:
#ifdef _DEBUG
   CDbgInstCount()   { reghelper.Add(id, 1); }
   CDbgInstCount(CDbgInstCount const &) {  reghelper.Add(id, 1); }
   ~CDbgInstCount()  { reghelper.Add(id, -1); }
#else
#endif
};

I just ned to add that to the classes in question, and have a look at the registry.

(The ID, if given as e.g. 'XYZ!' will be converted to a string. Unfortunately, you can't specify a string constant as template parameter)

peterchen
+1  A: 

A combination of boost::weak_ptr and boost::shared_ptr maybe? This article may be of interest.

dirkgently
+10  A: 

shared_ptr represents ownership relation. While weak_ptr represents awareness. Having several objects owning each other means you have problems with architecture, which is solved by changing one or more own's into aware of's (that is, weak_ptr's).

I don't get why suggesting weak_ptr is considered useless.

n0rd
If you could know ahead of time that there would be a single static owner, auto_ptr would be sufficient and you wouldn't need shared_ptr. I've always thought of shared_ptr as a "last one out, turn off the lights" mechanism.
Mark Ransom
Having shared_ptr to object means controlling that object's lifetime.Having a shared_ptr where weak_ptr would be sufficient means you probabaly have object's lifetime control in more places than you could suggest.
n0rd
+1  A: 

I know you said "no weak_ptr" but why not? Having your head with a weak_ptr to tail, and tail with a weak_ptr to head will prevent the cycle.

MighMoS
+2  A: 

See this post on detecting cycles in a graph.

Mr Fooz
+1  A: 

The generic solution to finding a cycle can be found here:

http://stackoverflow.com/questions/34249/best-algorithm-to-test-if-a-linked-list-has-a-cycle

This assumes that you know the structure of the objects in the list, and can follow all of the pointers contained in each object.

Mark Ransom
I would like to know how people specifically do that when using shared_ptr. It seems to me you need to add some overhead to make a directed graph out of all the shared_ptrs and keep track of their owners. I want to know the usual way to go about doing this.
Unknown
Shared_ptr doesn't keep a graph, just a count of the number of outstanding valid pointers. That's why you can't traverse it to find the cycle without outside knowledge of how the pointers are used.
Mark Ransom
@ Mark, my point is that you __need__ to create a directed graph of the shared_ptrs to collect cycles.
Unknown
Not neccessarilly, if you have the ability to retrieve "reflection" data for each class, you can simply start at one class, flag it as marked, and then move onto each pointer contained within the class, marking each as you go, then unmark it as you leave the recursive function. If the you ever encounter an already marked class, then you have found a loop.
Grant Peters
+2  A: 

I understand your annoyance at being glibly told to use weak_ptr to break cyclic references and myself I almost feel rage when I am told that cyclic references are bad programming style.

Your ask specifically how do you spot cyclic references. The truth is that in a complex project some reference cycles are indirect and difficult to spot.

The answer is that you should not make false declarations that leave you vulnerable to cyclic references. I am serious and I am criticizing a very popular practice - blindly using shared_ptr for everything.

You should be clear in your design which pointers are owners and which are observers.

For owners use shared_ptr

For observers use weak_ptr - all of them, not just those you think may be part of a cycle.

If you follow this practice then the cyclic references will not cause any problems and you don't need to worry about them. Of course you will have a lot of code to write to convert all these weak_ptrs to shared_ptrs when you want to use them - Boost really isn't up to the job.

John Morrison