Home > Back-end >  Pattern for inheriting and using a member variable in derived classes without casting each time
Pattern for inheriting and using a member variable in derived classes without casting each time

Time:03-03

I have a base class:

class Base{
protected:
    Storage* _storage;

    virtual void createStorage(){
        delete storage;
        _storage = new Storage();
    }

    void exampleUseOfBaseStorage(){
        _storage->baseData  ; //some complex calculation
    }
}

struct Storage{
    int baseData;
}

Each derived class has their own kind of storage:

struct DerivedStorage : Storage{
    int derivedData;
}

In the derived class,

class Derived{
protected:
    virtual void createStorage() override{
        delete storage;
        _storage = new DerivedStorage();
    }
}

Hence _storage can be used for all the base class members and the derived class members. The downside is, because for each derived class, I would have to cast the type of storage to DerivedStorage, or whatever type of storage the derived class uses, it is very tedious to type out the cast statement each time. Is there an elegant way around it ?

My solution is to just have one more variable of type DerivedStorage, and just use that in the derived classes member functions. Eg:

class Derived{
protected:
    DerivedStorage* _ds = nullptr;

    virtual void createStorage() override{
        delete storage;
        _storage = new DerivedStorage();
        _ds = _storage; // Use _ds in all member functions of Derived instead of _storage
    }

    void exampleUseOfDerivedStorage(){
         _ds->derivedData  ; //some complex calculation
    }
}

Is there a more elegant pattern to use for this use case?

CodePudding user response:

Because the method Base::exampleUseOfBaseStorage is protected, caller of this method is limited to the derivative classes (and Base).

So, if each derivative class has each storage as own member, they can use it as argument for Base::exampleUseOfBaseStorage.

class Base{
protected:
    //(This will be called from Drived)
    void exampleUseOfBaseStorage( Storage *storage );
};

CodePudding user response:

I would put the cast into a member function, rather than using a data member. The member function can have the same name in each derived class. If you really don't want to type out the function definition in each derived class, you could use CRTP to implement it.

Using a data member makes it impossible to safely use the rule-of-zero approach to the special member functions and you would have to implement all of them explicitly in each derived class.

I assume that the shown code is just abbreviated, but the base class has the same problem. If it was using std::unique_ptr<Storage> instead of a raw pointer it could simply follow the rule-of-zero and not bother implementing any of the special member functions, but as it is written in the question, the special member functions need to be explicitly defined to avoid causing UB when the class is copied/moved. (And also std::unique_ptr would make createStorage exception-safe, which it is currently not.)

  • Related