Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store weak pointer to self

I work with a codebase that was partially implemented by someone who was in love with overly complex solutions to simple problems (e.g. template classes with two parameters that were only ever instantiated for one pair of types). One thing she did was to create objects in a smart pointer, and then have the object store a weak pointer to itself.

class MyClass {
    //...
    boost::weak_ptr<MyClass> m_self;
    //...
};

boost::shared_ptr<MyClass>
Factory::Factory::Factory::CreateMyClass() {
    boost::shared_ptr<MyClass> obj(new MyClass(...));
    boost::weak_ptr<MyClass> p(obj);
    obj->storeSelfPointer(p);
    return obj;
}

The class then proceeds to use m_self by locking it and passing around the resulting shared pointer.

For the life of me, I cannot fathom what she was trying to accomplish. Is there some pattern or idea that would explain this implementation? It looks to me like this is completely pointless and I'd like to refactor it away.

EDIT: I should mention that none of the places that use the resulting smart pointer obtained from locking m_self actually retain the smart pointer.

like image 280
James Davidoff Avatar asked Jun 26 '14 00:06

James Davidoff


People also ask

When should I use Weak_ptr?

By using a weak_ptr , you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero.

How do you dereference a weak pointer?

but... you cannot dereference a weak pointer. you have to lock it, get a shared one from it and use that one. You are supposed to convert/create a shared_ptr from them before accessing the raw pointer to avoid race conditions.

What is weak PTR used for?

std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership.

How do you implement a weak pointer in C++?

To implement weak_ptr , the "counter" object stores two different counters: The "use count" is the number of shared_ptr instances pointing to the object. The "weak count" is the number of weak_ptr instances pointing to the object, plus one if the "use count" is still > 0.


1 Answers

A possible use of this "design" could be to use m_self.lock() to generate shared pointers from this.

If you remove this weak pointer member, the reference count hold by the generated shared pointer from this would be incorrect.

It achieves the same than std::enable_shared_from_this, interestingly enough, cppreference.com mentions this design :

A common implementation for enable_shared_from_this is to hold a weak reference (such as std::weak_ptr) to this. The constructors of std::shared_ptr detect the presence of an enable_shared_from_this base and assign the newly created std::shared_ptr to the internally stored weak reference

And the C++ standard, section § 20.8.2.4 10 , mention the same possible implementation :

The shared_ptr constructors that create unique pointers can detect the presence of an enable_shared_- from_this base and assign the newly created shared_ptr to its __weak_this member


Possible Refactoring :

  • If you are using C++11, you can remove the std::weak_ptr member, and publicly inherits from std::enable_shared_from_this<T>. You should retrieve a shared pointer from this by calling shared_from_this().

  • If you are not using C++11 but can use boost, use boost::enable_shared_from_this, see the boost documentation. You should retrieve a shared pointer from this by calling shared_from_this().

  • If you are not using C++11, and can't use boost, you can bring the proposed implementation of the standard to your code base, it is short enough :

Code : (copied from § 20.8.2.4 - 11, remove leading underscores, and you probably want to rename it)

template<class T> class enable_shared_from_this {
    private:
     weak_ptr<T> __weak_this;
    protected:
     constexpr enable_shared_from_this() : __weak_this() { }
     enable_shared_from_this(enable_shared_from_this const &) { }
     enable_shared_from_this& operator=(enable_shared_from_this const &) { return *this; }
     ~enable_shared_from_this() { }
    public:
     shared_ptr<T> shared_from_this() { return shared_ptr<T>(__weak_this); }
     shared_ptr<T const> shared_from_this() const { return shared_ptr<T const>(__weak_this); }
};

And use shared_from_this() to make a shared pointer. If you do copy this code, note that constructing shared pointers from this by other means would not work. The shared pointers constructors need to be modified (as explain by the standard quote above).

like image 110
quantdev Avatar answered Sep 30 '22 06:09

quantdev