mercredi 31 juillet 2019

Call common function in destructor before destroying derived members

Assuming we have the classical Base class and derived class like this

class B {
public:
    virtual ~B() {
        // calling it here is too late, see explanations
        //common_pre_cleanup_function();
    }

    void common_pre_cleanup_function() { }
};

class D : public B {
public:
    virtual ~D() {
        // What if we forget to do this call in another derived class?
        common_pre_cleanup_function();
    }
};

How would you make sure a function like common_pre_cleanup_function() is called in all derived Ds destructors before the members of D are destroyed but without having to explicitly call this function in every destructor-implementation of a new D?

Background

In my current project we have a base class that implements certain parallelism and threading features and will eventually start a new thread that does the actual work. In the destructor of this base class we wanted to make sure, that the thread is always stopped and joined so that it gets cleaned up properly.

However derived classes may create members that are used by this thread in the base class. So if we destroy objects of the derived class, these members are also destroyed. But at this time the thread that is managed by the base class can still be running and now wrongfully access destroyed members.

I'm aware that this isn't the smartest approach to solve the issue and probably splitting up the threading/parallelisation parts and the "actual work" parts into separate classes might be the much smarter idea. However I'm interested if there are any approaches that don't involve an entire rewrite of the existing code base.

This code here is closer to our situation

class BackgroundTask {
public:
    virtual ~BackgroundTask() {
        // if we forget to call stop() in the derived classes, we will
        // at this point have already destroyed any derived members
        // while the thread might still run and access them; so how/where
        // can we put this call?
        //stop();
    }

    void stop() {
        cancelFlag_.set();
        thread_.join();
    }

    // more functions helping with Background tasks

private:
    Thread thread_;
    Condition cancelFlag_;
};

class MyTask : public BackgroundTask {
public:
    virtual ~MyTask() {
        // with the current case, we have to remember to call
        // this function in all destructors in classes derived
        // from BackgroundTask; that's what I want to avoid
        stop();
    }

private:
    std::unique_ptr<MyClass> member;
};

Aucun commentaire:

Enregistrer un commentaire