#include <iostream>
#include <tr1/memory>
class Circular
{
public:
typedef std::tr1::shared_ptr<Circular> Ptr;
~Circular() { std::clog << "Destroying a circular instance.\n"; }
void SetReference(const Ptr& ptr) { m_ptr = ptr; }
private:
Ptr m_ptr;
};
int main()
{
Circular::Ptr first = Circular::Ptr(new Circular());
Circular::Ptr second = Circular::Ptr(new Circular());
first->SetReference(second);
second->SetReference(first);
return 0;
}
No output is produced when the program is run. This implies that the
destructors for the allocated objects are never called, and the memory
allocated is never freed up causing a memory leak.
The cause for this behavior is due to the circular references that
exist between the two instances.
How can someone ensure that memory is freed up when circular references
like this exist between tr1::shared_ptr's?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
>
> How can someone ensure that memory is freed up when circular references
> like this exist between tr1::shared_ptr's?
>
Simple: don't do it. <g>
You have to break the cycle. You can do that explicitly, by re-assigning
one of the shared_ptr objects to hold another resource (most likely with
a null pointer), or you can use a weak_ptr object instead of a
shared_ptr object as one of the links.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
>
> The cause for this behavior is due to the circular references that
> exist between the two instances.
>
> How can someone ensure that memory is freed up when circular references
> like this exist between tr1::shared_ptr's?
You might try the version of a "in development" boost policy_ptr tested
in:
the test with enumerator:
std_shared_graph_accepting
is precise and works with containers. The one with:
std_shared_graph_tagged
does not.
Preliminary docs are at:
HTH.
I am perhaps guessing here too much, but IMHO you need to use weak_ptr
references between those two objects, instead of shared_ptrs.
WW aka Attila
Once the program exits, the OS should free up all the memory the
program received. Obviously, the destructors won't be called in that
case. You may be able to avoid the cycles by using weak pointers
(tr1::weak_ptr) or by more careful design work.
In a pinch, and if the problem is limited to just a specific class in
your application, you could have objects of this class self-register in
some sort of pool (storing a weak_ptr to registrants). Free up cycles
using the pool by constructing a digraph based on inter object
relationships. Then use the use_count() to figure out if you have any
orphaned cycles. Of course, other parts of the application may still
be holding weak_ptrs to these orphans.
It would be easiest to simply reevaluate your design.
- Niek Sanders
http://www.cis.rit.edu/~njs8030/
If your design expects circular references to be present, then using
tr1::shared_ptr as a data member is not the right tool. You should use
tr1::weak_ptr instead.
HTH,
Ganesh
An alternative to the use of weak_ptr is to move memory management
concerns to a layer below the one that has circular references. Your
example could be written as
#include <iostream>
#include <tr1/memory>
class Circular
{
public:
Circular() : m_ptr(0) {}
~Circular() { std::clog << "Destroying a circular instance.\n"; }
void SetReference(Circular * ptr) { m_ptr = ptr; }
private:
Circular * m_ptr;
};
typedef std::tr1::shared_ptr<Circular> CircularPtr;
int main()
{
std::pair<CircularPtr, CircularPtr> p;
p.first = CircularPtr(new Circular());
p.second = CircularPtr(new Circular());
p.first->SetReference(p.second);
p.second->SetReference(p.first);
return 0;
}
As the lifetime of both instances of Circular are now controlled by
std::pair you don't need to handle ownership/destruction within
circular itself.
Cheers,
Nicola Musatti
> An alternative to the use of weak_ptr is to move memory management
> concerns to a layer below the one that has circular references. Your
> example could be written as
>
> #include <iostream>
> #include <tr1/memory>
>
> class Circular
> {
> public:
> Circular() : m_ptr(0) {}
> ~Circular() { std::clog << "Destroying a circular instance.\n"; }
> void SetReference(Circular * ptr) { m_ptr = ptr; }
>
> private:
> Circular * m_ptr;
> };
>
> typedef std::tr1::shared_ptr<Circular> CircularPtr;
>
> int main()
> {
> std::pair<CircularPtr, CircularPtr> p;
> p.first = CircularPtr(new Circular());
> p.second = CircularPtr(new Circular());
>
p.first->SetReference(p.second.get());
p.second->SetReference(p.first.get());
>
> return 0;
> }
It is much safer to use weak_ptr's and weak_ptr::lock() to get a
shared_ptr when one wants to access via m_ptr.
void Circular::use_ptr
{
boost::shared_ptr<Circular> p,q;
p = m_ptr.lock();
if(p!=q)
{
// pointer is valid ,use p to access data.
}
else
{
// pointer is invalid do something else
}
}
that something else might be do nothing if that is appropriate. The
point is that using raw pointers using Circular::m_ptr has no way to
know if the pointer has been deleted without its knowledge, but
weak_ptr provides this information.
The following idiom will save you a few keystrokes:
void Circular::use_ptr()
{
if(boost::shared_ptr<Circular> p = m_ptr.lock())
{
// pointer is valid, use p to access data.
}
else
{
// pointer is invalid do something else
}
}
HTH,
Ganesh
My point is exactly to remove the need for weak_ptr's by centralizing
pointer management for a given context/subsystem. In this way within
that context you can assume that pointers are always valid.
Cheers,
Nicola Musatti