c++shared-ptrweak-ptr

Can I reset shared_ptr without deleting object so that weak_ptr loses a reference to it


I'd like to reset a shared_ptr without deleting its object and let weak_ptr of it loses a reference to it. However, shared_ptr doesn't have release() member function for reasons, so I can't directly do it. The easiest solution for this is just to call weak_ptr's reset() but the class which owns the shared_ptr and wants to release it doesn't know which class has weak_ptr of it. How can that be achieved in this case?

I understand why shared_ptr doesn't have release function but unique_ptr. In my case, only one instance owns a pointer, but shared_ptr can be owned by multiple instances and releasing doesn't make sense then. But if shared_ptr doesn't have that function, how can I cut connections to weak_ptr without deleting the object?

shared_ptr<int> A = make_shared<int>(100);
weak_ptr<int> B = A;

// I want something like this! but shared_ptr doesn't have release function..
int* releasedData = A.release();
A.reset(releasedData);

if (shared_ptr<int> C = B.lock)
{
    // B already lost a reference, so it should never reach here
}

Background

A shared pointer of a class which stores a pointer of a large array is shared with other classes. The shared pointer is passed as a weak pointer to those, and the owner class of the shared pointer doesn't know about those classes. Multiple instances of the owner class are activated and deactivated at runtime. Because initializing cost of the instance is high, I use object pool pattern: I reuse those instances instead of creating/deleting every time I use it. The problem here is that when an instance of the owner class is deactivated, it should be tread as removed(although it still holds the data), and thus other classes which has a weak reference to the data should lose the reference. Resetting a shared pointer makes it possible, but I don't want to do it because the data is large.

I can make a manager class to keep track of which weak pointer refers to which shared pointer, but I wonder if this can be solved by something else.


Solution

  • std::shared_ptr and std::weak_ptr are made to model the concept of RAII. When you use a std::shared_ptr, you already made the decision that it owns the resource, and that it should release the resource on destruction. The clunkiness you face is due to a rebellion against RAII: you want std::shared_ptr to own the resource but sometimes not really.

    The solution is to reframe your object pool as an allocator. Suppose you have

    struct obj_t { /* ... */ };
    
    struct pool_t {
        obj_t* acquire();      // get an object from the pool
        void release(obj_t*);  // return the object to the pool
        // ...
    };
    

    Then your std::shared_ptr will look like

    auto d = [&pool](auto p){ pool.release(p); };
    std::shared_ptr<obj_t> obj{pool.acquire(), d};
    

    Notably, the resource acquisition always gets paired with its destruction. Under this model your problem doesn't exist

    std::weak_ptr<obj_t> b = obj;
    obj = nullptr;  // or let obj get destroyed in any other way
    
    if(auto c = b.lock())
        // doesn't happen