Question

Please consider this class hierarchy:

#include <iostream>

struct Base
{    
    Base(int arg1, int arg2, int arg3, int arg4)
    {
        std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
    }
    void BaseDoWork()
    {
        std::cout << "Base::BaseDoWork" << std::endl;
    }
};

struct Derived1:public Base
{
    Derived1(int arg1, int arg2, int arg3, int arg4):Base(arg1, arg2, arg3, arg4){}
    void DerivedDoWork()
    {
        BaseDoWork();
    }
};

struct Derived2:public Derived1
{
    Derived2(int arg1, int arg2, int arg3, int arg4):Derived1(arg1, arg2, arg3, arg4){}
};

int main(int argc, char *argv[])
{
    Derived2 myDerived2(1,2,3,4);
    myDerived2.DerivedDoWork();
    return 0;
}

The class Derived1 has more coupling than I would like: it has to know how to construct a "Base" class. In a deep hierarchy this would mean propagating the arguments for the "Base" class constructor all the way down the class hierarchy. A change to the "Base" class constructor would require a change to all the derived classes.

My objectives for this class hierarchy are:

  1. Derived1 expects a BaseDoWork() method in the base class but does not need to know how to construct the base class.

  2. Derived2 knows how to construct a "Base" class.

Ideally I'd like to make Derived1 a template accepting any base class with an accessible "BaseDoWork()" method, along these lines:

template <T> struct Derived1:public T
{
    void DerivedDoWork()
    {
        BaseDoWork();
    }
};

But if I instantiate the above template as Derived1<Base> I can't see how to construct "Base" class without adding knowledge of "Base" constructor arguments to Derived1.

Was it helpful?

Solution

Assuming that this is the real hierarchy, if Derived1 is a Base derived class, must know how to contruct a parent class (Base).

As I understood, maybe I'm wrong, if Derived1 is not really a Base derived (child) class, and only needs to know it's interface, there is no need to know how to construct a Base instance. So you can define Derived1 as follows:

struct Derived1
{
    Derived1(int arg1, int arg2, int arg3, int arg4)
    {}

    .....
};

Only Base derived classes need to know how to contruct Base:

struct Derived2 : public Base
{
    Derived2(int arg1, int arg2, int arg3, int arg4) : 
        Base(arg1, arg2, arg3, arg4)
    {}

    .....
};

Now, if Derived2 is really a Derived1 child, must know how to create it's parent. Previous code became:

struct Derived2 : public Base, public Derived1
{
    Derived2(int arg1, int arg2, int arg3, int arg4) : 
        Base(arg1, arg2, arg3, arg4),
        Derived1(arg1, arg2, arg3, arg4)
    {}

    .....
};

If you must use Base::BaseDoWork() method from Derived1, you may define classes as follows:

struct Base
{    
    Base(int arg1, int arg2, int arg3, int arg4)
    {
        std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
    }

    virtual void BaseDoWork()
    {
        std::cout << "Base::BaseDoWork" << std::endl;
    }
};

struct Derived1
{
    Derived1(int arg1, int arg2, int arg3, int arg4)
    {}

    template <typename T>
    void DerivedDoWork(T & base)  // only knows T interface
    {
      base.BaseDoWork();
    }
};

struct Derived2 : public Base, public Derived1
{
    Derived2(int arg1, int arg2, int arg3, int arg4) : 
      Base(arg1, arg2, arg3, arg4),
      Derived1(arg1, arg2, arg3, arg4)
    {}

    virtual void DerivedDoWork()
    {
      Derived1::DerivedDoWork<Base>(*(static_cast<Base *>(this)));
    }
};

Main keeps the same.

May you need to review this hierarchy.

OTHER TIPS

When you make public inheritance of Derived1 from Base, the result is that Derived1 already knows how to construct Base, so if you want Derived1 not know how to construct Base, maybe it's better to not inherit it from Base? Instead, separate Base as some kind of performer.

Consider this piece of code, I don't claim that this is solution, but maybe it will push you to other thoughts (the names of classes are the same):

#include <iostream>

struct Base
{
    Base()
    {
        std::cout << "Base::Base:" << std::endl;
    }
    Base(int arg1, int arg2, int arg3, int arg4)
    {
        std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
    }
    void DoWork()
    {
        std::cout << "Base::BaseDoWork" << std::endl;
    }
};

template <typename T>
struct Derived1
{
    T object;

    Derived1(T obj){ object = obj; }
    void DerivedDoWork()
    {
        object.DoWork();
    }
};

template <typename T>
struct Derived2 : public Derived1<T>
{
    Derived2(int arg1, int arg2, int arg3, int arg4) : Derived1( T(arg1, arg2, arg3, arg4))
    { }
};

int main(int argc, char *argv[])
{
    Derived2<Base> myDerived2(1,2,3,4);
    myDerived2.DerivedDoWork();
    return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top