How to keep one object alive as long as another object exists?

629 views Asked by At

Is there a way to prevent one particular object of being destroyed while another object still exists, without explicitly making the class of the second one to know about the first?

Here is an example:

class A { /* A creates B*/ };
class B {};

int main() {
    auto spA = std::make_shared<A>();
    auto spB = spA->getB();

    spA.reset();
    spB->DoSomething(); 
}

After spA.reset(), I would like to have at least one reference to the object pointed by spA. Or, in other words: I would like to destroy the object pointed by spA only after calling spB->DoSomething().

But, an important prerequisite is B is not allowed to "know" about A (e.g. it is not allowed to hold a pointer/reference to it).

As I understand, shared pointers provide some functionality which can help with dealing with this problem, but I am not sure which one.

EDIT:

The brief structure of A and B look as follows:

class A 
{
public:
    A() 
    { 
        m_B = B::FactoryB(); 
    }
    std::shared_ptr<B> getB()
    {
        return m_B;
    }
private:
    std::shared_ptr<B> m_B; 
};

class B 
{
public:
    static std::shared_ptr FactoryB()
    {
        return std::make_shared<B>();
    }
};

EDIT2 (MY SOLUTION):

So, using suggestion provided by @MSalters I was able to find a solution that works for me. In this case it was much simpler than expected, such that I only needed to adjust A::getB():

    std::shared_ptr<B> getB()
    {
        std::shared<B> spBAlias(this->shared_from_this(), m_B.get());
        return spBAlias;
    }

Here, instead of just returning m_B, A::getB() creates and returns a shared pointer spBAlias of type B (using alias constructor), which influences the reference counting of the A object (provided by this->shared_from_this()). So, calling spA.reset() in the main decreases uses of spA from two to one, making the call of spB->DoSomething() possible. To use shared_from_this(), A needs to inherit from std::enable_shared_from_this<A>:

class A : public std::enable_shared_from_this<A> { /* ... */ }
2

There are 2 answers

0
MSalters On BEST ANSWER

shared_ptr has something called an aliasing constructor. A shared_ptr<B> can be created from a shared_ptr<A> and a B*. The destructor of this shared_ptr<B> will delete the A object, not the B.

In your case, your A object could itself hold a unique_ptr<B>, or even an outright B member.

2
Useless On

You can absolutely do this using shared_ptr, but you'll have to write some extra code.

Just have getB return a shared_ptr<B> with a custom deleter ... the deleter should be a lambda capturing a shared_ptr<A> by value, and explicitly releasing that only when spB goes out of scope and tries to delete the B object.

A will have to derive from std::enable_shared_from_this to get a viable shared pointer to itself from inside A::getB().

If the B object is actually a subobject of A, then this is sufficient. Otherwise, you should really delete the B as well.


int main() {
    shared_ptr<A> spA = std::make_shared<A>();
    shared_ptr<B> spB = spA->getB();

    spA.reset();         // spB's deleter keeps the refcount nonzero
    spB->DoSomething();  // fine
}                        // spB's destructor finally deletes *spA