Weak pointer to object not managed by a shared_ptr

Hi, One of the smart pointer techniques [1] explains use of weak_ptr pointer to object not managed by a shared_ptr. [1] http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/sp_techniques.html I'm wondering what would be practical use case of that? Is it a part of any of known idioms or patterns? Also, is it valid to assume that this technique can extend lifetime of X so of the internal integral X::i_ ? Here is a simple test case based on the technique explained in the smartptr docs [1] with my questions marked using XXX tag: /// begin of program /////////////////////////////////////////////// #include <exception> #include <iostream> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> using namespace std; using namespace boost; struct null_deleter { void operator()(void const *) const { std::cout << "deleter\n"; } }; class X { public: explicit X(int i): this_(this, null_deleter()), i_(i) {} X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_) {} X & operator=(X const & rhs) { i_ = rhs.i_; return *this; } weak_ptr<X> get_weak_ptr() const { return this_; } int get_i() const { return i_; } private: shared_ptr<X> this_; int i_; }; int main() { try { 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; } catch (bad_weak_ptr const& e) { cerr << e.what() << endl; } catch (exception const& e) { cerr << e.what() << endl; } catch (...) { cerr << "unknown error\n"; } return 0; } /// end of program /////////////////////////////////////////////// No exception is thrown and I can It seem that the lifetime of x1, automatic object, has been extended beyond its "natural" scope. Am I correct? However, if I replace retrival of shared_ptr with weak_ptr then of course bad_weak_ptr exception is thrown: shared_ptr<X> spx; weak_ptr<X> wpx; { X x1(7); wpx = x1.get_weak_ptr(); } spx = shared_ptr<X>(wpx); cout << spx->get_i() << std::endl; I'm having problems with understanding of what causes the extention of lifetime of X (so of internal X::i_ object). Is this mechanism similar to binding r-value to "const reference"? I would appreciate if anyone could help me to understand it. References to the C++ standard would be great too. Best regards, -- Mateusz Loskot, http://mateusz.loskot.net Charter Member of OSGeo, http://osgeo.org

Mateusz Loskot <mateusz <at> loskot.net> writes:
One of the smart pointer techniques [1] explains use of weak_ptr pointer to object not managed by a shared_ptr.
[1] http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/sp_techniques.html
Interesting, I had never read, nor thought of that before.
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.
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. The safety is questionable though in a multi-threaded environment unless you also block the destruction when creating the shared_ptr from the weak_ptr. It's best if you design accounts for object lifetimes in associated classes.
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. 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. 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. <snip>
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. 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. <snip>
However, if I replace retrival of shared_ptr with weak_ptr then of course bad_weak_ptr exception is thrown:
shared_ptr<X> spx; weak_ptr<X> wpx; { X x1(7); wpx = x1.get_weak_ptr(); } spx = shared_ptr<X>(wpx); cout << spx->get_i() << std::endl;
This is more proper usage (though not very helpful here). <snip>
I would appreciate if anyone could help me to understand it. <snip>
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. Perhaps someone else can give a better, more realistic, example though, as even this one I wouldn't code this way. HTH, -Ryan

Am Wednesday 02 September 2009 18:55:03 schrieb Ryan Gallagher:
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.
Perhaps someone else can give a better, more realistic, example though, as even this one I wouldn't code this way.
I actually use that technique (without knowing the linked document). but I can't think of very many other use cases: My case handles a lot of objects that are not required to stay in memory but are also not supposed to be destroyed immediatly after they've been used, so they're not constantly re-created if they're used more than once (loaded back from disk in my case). class cache_element : public intrusive::slist_base_hook<>{ ... private: shared_ptr<cache_element> this_; ... }; class object : cache_element{ ... }; the objects are only referenced by weak_ptr's throughout the code, and loaded back from disk if weak_ptr::lock() fails. so the only reference keeping the object in memory is an object cache, which fills cache_element::this_ on first access and removes the element from the list of cached elements when the cache overflows, using a disposer like that: struct disposer{ void operator()(cache_element *element){ shared_ptr<cache_element> ptr; ptr.swap(element->this_); } };

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@dog:~$ g++ -Wall -pedantic weak_ptr_local.cpp mloskot@dog:~$ ./a.out X dtor 7 deleter mloskot@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

On Wed, Sep 2, 2009 at 1:18 PM, Mateusz Loskot<mateusz@loskot.net> wrote:
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@dog:~$ g++ -Wall -pedantic weak_ptr_local.cpp mloskot@dog:~$ ./a.out X dtor 7 deleter mloskot@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!
Bah, x1 exists on the stack, its memory is not free'd after its destructor is run, hence when you access get_i, it still return 7. Try adding this right before that ->get_i call: char buf_on_stack[255]; // or however large X is, plus some for just in case... memset(&buf_on_stack, 255, size_of(buf_on_stack)); Then notice that your get_i call does not return 7, rather it returns part of that array, which is filled with all 1's.

OvermindDL1 wrote:
Bah, x1 exists on the stack, its memory is not free'd after its destructor is run, hence when you access get_i, it still return 7.
Yes, it's been perfectly clear for me. Perhaps I used some shortcuts in my explanation.
Try adding this right before that ->get_i call: char buf_on_stack[255]; // or however large X is, plus some for just in case... memset(&buf_on_stack, 255, size_of(buf_on_stack));
Then notice that your get_i call does not return 7, rather it returns part of that array, which is filled with all 1's.
Just for records, it doesn't cause such effect for Visual C++ 9.0. However, it doesn't change the fact of UB we've already discussed and agreed. Best regards, -- Mateusz Loskot, http://mateusz.loskot.net Charter Member of OSGeo, http://osgeo.org

Mateusz Loskot wrote:
simple testes using GCC 4.3 and Visual C++ 9.0 show that destruction occurs in different point than we expect:
Perhaps it is different than you expected.
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@dog:~$ g++ -Wall -pedantic weak_ptr_local.cpp mloskot@dog:~$ ./a.out X dtor 7 deleter mloskot@dog:~$
So, x1 is destructed but its value of 7 is still accessible.
x1 went out of scope. A shared_ptr referencing its address does nothing to extend it's lifetime.
I assume I'm observing UB here. I understand nature of UB and that it can give unexpectedly 'correct' results.
As Overmind showed, the stack has yet to be overwritten by that point, so the bits exist. What is undefined is accessing what used to be an object after its destructor ran. That you can see the value is due to X::~X() not overwriting it and of the data member type not having a destructor that likewise does something destructive. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Mateusz Loskot wrote:
Hi,
One of the smart pointer techniques [1] explains use of weak_ptr pointer to object not managed by a shared_ptr.
[1] http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/sp_techniques.html
I'm wondering what would be practical use case of that? Is it a part of any of known idioms or patterns?
I know, it's not a design guideline to follow but I used the technique to implement pointers that may be checked for being dangling. I needed this to quick fix a problem in a quite complicated code that sometimes crashed due to raw dangling pointers.

Andrey Semashev wrote:
Mateusz Loskot wrote:
Hi,
One of the smart pointer techniques [1] explains use of weak_ptr pointer to object not managed by a shared_ptr.
[1] http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/sp_techniques.html
I'm wondering what would be practical use case of that? Is it a part of any of known idioms or patterns?
I know, it's not a design guideline to follow but I used the technique to implement pointers that may be checked for being dangling. I needed this to quick fix a problem in a quite complicated code that sometimes crashed due to raw dangling pointers.
The detection of dangling pointers is a good example of practical use case of this guideline. It looks like it relates to the case in serialization library pointed by Robert Ramey. I see some light :-) Best regards, -- Mateusz Loskot, http://mateusz.loskot.net Charter Member of OSGeo, http://osgeo.org

Mateusz Loskot wrote:
Hi,
One of the smart pointer techniques [1] explains use of weak_ptr pointer to object not managed by a shared_ptr.
[1] http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/sp_techniques.html
I'm wondering what would be practical use case of that? Is it a part of any of known idioms or patterns?
FWIW the serialization library needed to use this to detect destruction of dependent objects whose destruction was handled automatically. I'm guessing this would qualify as a use case. Robert Ramey
participants (7)
-
Andrey Semashev
-
Mateusz Loskot
-
OvermindDL1
-
Robert Ramey
-
Ryan Gallagher
-
Stefan Strasser
-
Stewart, Robert