Home > Software engineering >  How to delete an Object whose associating use_count is bigger than 1?
How to delete an Object whose associating use_count is bigger than 1?

Time:12-09

I have an Object with multiple shared_ptrs pointing to it, and its reference count use_count in the associating control block is bigger than 1.

Now, I want to deconstruct the Object, but I do not know where are all those shared_ptrs, so I cannot find and deconstruct them before I deconstruct the Object.

If I just deconstruct the Object, it will make those shared_ptrs become dangling. Therefore, under this situation, how to delete the Object with use_count bigger than 1 but have no idea about all its shared_ptrs?

Thanks for any suggestions!

CodePudding user response:

How to delete an Object whose associating use_count is bigger than 1?

By reducing the use count to 0. This can be achieved by destroying or reassigning the shared pointers that currently point to the object.

but I do not know where are all those shared_ptrs

Then you must solve the problem that prevents you from knowing all those pointers.

Or, you could consider that perhaps there is a reason why those pointers exist, and that perhaps the object isn't supposed to be deleted yet, since it's apparently being used by some part of the program.

CodePudding user response:

If you have access to code of Object class and can modify it then you can do following steps (you may jump straight away to final code afterwards):

  1. Create special structure Fields that contains all fields of original Object.

  2. Store Fields as field of std::unique_ptr type, allocated in Heap.

  3. In original Object class make all original fields as references pointing to fields of Fields heap allocated object.

  4. Add destroyed_ bool flag that marks that Object was already destroyed. This flag becomes true after first call of destructor.

  5. In every method check that destroyed_ is not true, otherwise throw an exception. Because NONE of methods can be used when object is already destroyed. You may also just show a message with some error instead of throwing exception and return from method without doing anything. Up to you how to handle this error.

  6. Make destructor non-virtual! It is important because when object is destroyed its virtual table pointer becomes ill-formed, hence you can't have virtual destructor. Actually all virtual methods also can't be called. Maybe I'm wrong and modern compilers don't destroy virtual table anymore, some exports opinion is needed here.

  7. Inside destructor on first call make all cleanup as usual. And mark destroyed_ as true. Second call to destructor should just silently exit due to destroyed_ being already true.

  8. All copy constructors and assignment operators should be implemented as usual. Example in code below.

Try it online!

#include <iostream>
#include <memory>

class Object {
public:
    Object()
        : p_(new Fields{}), f0_(p_->f0_), f1_(p_->f1_) {}

    Object(Object const & o)
        : p_(new Fields{}), f0_(o.f0_), f1_(o.f1_) {}
    
    Object & operator = (Object const & o) {
        f0_ = o.f0_;
        f1_ = o.f1_;
        return *this;
    }

    void DoSomething() {
        if (destroyed_)
            throw std::runtime_error("Object already destroyed!");
        f0_  = 1;
        f1_  = std::to_string(f0_)   " ";
        std::cout << "DoSomething: '" << f1_ << "'" << std::endl;
    }

    ~Object() {
        if (destroyed_) {
            std::cout << "Called destructor of destroyed object..."
                << std::endl;
            return;
        }
        // Process fields cleanup here...
        destroyed_ = true;
    }

private:
    struct Fields {
        int f0_ = 0;
        std::string f1_;
    };

    std::unique_ptr<Fields> p_;
    bool destroyed_ = false;

    int & f0_;
    std::string & f1_;
};

int main() {
    try {
        std::shared_ptr<Object> o0 = std::make_shared<Object>();
        {
            std::shared_ptr<Object> o1 = o0;
            o1->DoSomething();
            o1->DoSomething();
            // Call destructor when you don't need object.
            o1->~Object();
        }
        o0->DoSomething();
        return 0;
    } catch (std::exception const & ex) {
        std::cout << "Exception: " << ex.what() << std::endl;
        return -1;
    }
}

Output:

DoSomething: '1 '
DoSomething: '1 2 '
Called destructor of destroyed object...
Exception: Object already destroyed!
  • Related