Search code examples
c++constructordecoratormixins

Is this a valid workaround to the constructor issue with mixins?


As I understand there is a problem with mixins where, if you want to use something other than a no-arg constructor, the objects you use your mixin on will either have to have a common constructor signature or you have to use initializer methods in the classes you want to use the mixin with.

This seems to be kind of a workaround, though I'm not sure if there are situations in which it would fail. It makes the mixin work more like a decorator, but it removes the need for the common interface that the decorators inherit from. I guess another problem is that the syntax might get clunky?

Just want to know if there is anything horribly dangerous about this. I also would like to know if I, being a not so smart programmer, have misunderstood this problem with mixins.

Edit: this appears to be a better formulation.

class Base
{
protected:
    std::string name;
public:
    Base()
    {
        name = "no arg constructor";
    }
    Base(std::string n)
    {
        name = n;
    }

    virtual void doSomething()
    {
        cout << name << "\n";
    }
};

class Derived : public Base
{
private:
    int x;
public:

    Derived(std::string n, int i) : Base(n)
    {
        x = i;
    }

    void doSomething()
    {
        cout << "x = " << x << "\t";
        Base::doSomething();
    }
};

template <class T>
class Decorator : public T
{
public:
    Decorator(const T& initializer) : T(initializer)
    {
        //*static_cast< T* >(this) = *initializer;
        //delete initializer;
    }
};

void method(Base& b)
{
    b.doSomething();
}

int main()
{
    Base b;
    Decorator<Base> d1(b);
    Decorator<Base> d2(Base("decorated"));
    Decorator<Derived> d3(Derived("derived", 777));

    method(d1);
    method(d2);
    method(d3);

    return 0;
}

no arg constructor

decorated

x = 777 derived


Solution

    1. You're leaking memory if an exception is thrown during construction,
    2. The base class must have a default constructor to be be constructed first and then assigned.

    Better do:

    Decorator(const T& initializer) : T(initializer)
    {
    }
    

    and use:

    Decorator<Base> d1((Base()));
    Decorator<Base> d2(Base("decorated"));
    Decorator<Derived> d3(Derived("derived",777));
    

    Also in C++0x you can forward perfectly any number of arguments from Decorator's constructor to the base constructor, effectively making the usage as clean as usual:

    Decorator<Base> d1;
    Decorator<Base> d2("decorated");
    Decorator<Derived> d3("derived",777);