Boost logo

Boost :

Subject: Re: [boost] Weak pointer to object not managed by a shared_ptr
From: Mateusz Loskot (mateusz_at_[hidden])
Date: 2009-09-02 15:18:36


Ryan Gallagher wrote:
> Mateusz Loskot <mateusz <at> loskot.net> writes:
>> I'm wondering what would be practical use case of that?
>
> I think the use case is for when you have an object that needs to
> reference an X instance (from your example) but doesn't have any
> knowledge of the lifetime of that X instance. Thus, instead of
> using a C++ reference or pointer that object can hold a
> weak_ptr<X> -- enabling it to check if the X instance is still
> alive when it needs to use it.

I see. IOW, the idea is to make a weak association between objects
and avoid crashes caused by dereferencing invalid pointer.

>> Is it a part of any of known idioms or patterns?
>
> I don't think so. I think this use-case (referencing but not knowing
> the lifetime) is something that should probably be avoided in one's
> design. However, if it's impractical to do so then this would
> just make it safer than using a raw pointer or C++ reference.

Right.

>> Also, is it valid to assume that this technique can extend lifetime of X
>> so of the internal integral X::i_ ?
>
> No. The example you have is undefined behavior as the X instance
> is destructed when it goes out of scope.

The confirmation of UB is enough for me, however...

> Your just using the
> shared_ptr<X> to reference the destroyed X instance. This is the
> same as if you had held a raw pointer to that X instance after it
> was destructed.

simple testes using GCC 4.3 and Visual C++ 9.0 show that destruction
occurs in different point than we expect:

1) I added verbose destructor to X

~X() { cout << "X dtor\n"; }

2) Compiled and executed the following use case of X:

shared_ptr<X> spx;
{
    X x1(7);
    spx = shared_ptr<X>(x1.get_weak_ptr());
} // *** x1 is being destructed
cout << spx->get_i() << std::endl;

mloskot_at_dog:~$ g++ -Wall -pedantic weak_ptr_local.cpp
mloskot_at_dog:~$ ./a.out
X dtor
7
deleter
mloskot_at_dog:~$

So, x1 is destructed but its value of 7 is still accessible.
I assume I'm observing UB here. I understand nature of UB and that
it can give unexpectedly 'correct' results.

Shortly, this is UB and one should always assume the point of x1
object destruction (marked with ***) is when the execution
leaves the scope in which x1 was declared.

Is this correct?

> The whole point is to just hold a weak_ptr<X> until you actually
> need to use it. At that point you can check whether the X instance
> is still alive.

So, the weak_ptr acts as a flag indicating if pointee is still
alive (a self-indicator). Alternative would be to cache some boolean
indicator and check it instead of the weak_ptr, before accessing
object of X.

>> shared_ptr<X> spx;
>> {
>> X x1(7);
>>
>> // XXX: lifetime is extended here?
>> spx = shared_ptr<X>(x1.get_weak_ptr());
>> }
>>
>> // XXX: No exception. x1 is still alive, so the pointer valid?
>> cout << spx->get_i() << std::endl;
>
> x1 is not alive, it was destructed at scope exit.

Yes, this is clear.

> The shared_ptr<X> that it held was also destructed at this point.
> You just kept the ref-count up through spx even though it refers
> to a destroyed object. Check the ref-count at this point, it
> should be 1.

Yes, however get_i() returns the value stored in x1. It's UB anyway.

> For using this, think about X lifetimes being managed by some other
> class (XManager). Think about another class A that isn't managed
> my XManager but needs to weakly reference some X instance.

Yes, I understand this kind of use cases. It makes sense to me.

> Perhaps someone else can give a better, more realistic, example
> though, as even this one I wouldn't code this way.

I wouldn't code this way too.

Thanks all for helping me to understand it better!

Best regards,

-- 
Mateusz Loskot, http://mateusz.loskot.net
Charter Member of OSGeo, http://osgeo.org

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk