Two members, default initialized in Base, possibly non-default initialized in Derived

64 views Asked by At

I have a base class with a default constructor.

class Base
{
public:
   Base();
private:
   Type1 m_ofType1;
   Type2 m_ofType2;
}

Base::Base()
   : m_ofType1(defaultExpr1)
   , m_ofType2(defaultExpr2)
{
}

And I have a derived class, which may be:

  • default constructed, with m_ofType1 and m_ofType2 being initialized by their respective default expressions,
  • provided with a Type1 only, or
  • provided with a Type2 only.
class Derived : public base
{
public:
   Derived(); // m_ofType1 and m_ofType2 will be initialized with their default expressions.

   Derived(Type1 overrideOfType1); // m_ofType1 will be initialized with overrideOfType1 and m_ofType2 will be initialized with its default expression.

   Derived(Type2 overrideOfType2); // m_ofType1 will be initialized with its default expression and m_ofType2 will be initialized with overrideOfType2.
}

Base is for Production code and Derived is for test code. I would like to implement the constructors without data or code duplication, but I can't see how.

Given the constraints, which I hope have been made clear, do you all know how to implement the above constructors without data or code duplication?

Example of code duplication:

We could add protected constructors to base:

Base(Type1);
Base(Type2);

That means that the Derived constructors would just forward the call to their respective Base constructors.

Derrived::Derrived()
   : Base()
{
}

Derived::Derived(Type1 overrideOfType1)
   : Base(overrideOfType1)
{
}

Derived::Derived(Type1 overrideOfType2)
   : Base(overrideOfType2)
{
}

In doing this, the question changes a little. How do you implement the Base constructors without data or code duplication.

Here's my best attempt at doing this. It doesn't work, and I'll show you why.

First, add another constructor to Base:

Base(Type1 ofType1, Type2 ofType2);

Base::Base() : Base(defaultExpr1, defaultExpr2) { }

Base::Base(Type1 overrideOfType1) : Base(overrideOfType1, defaultExpr2) { }

Base::Base(Type2 overrideOfType2) : Base(defaultExpr1, overrideOfType2) { }

Base::Base(Type1 ofType1, Type1 ofType2) : m_ofType1(ofType1) , m_ofType2(ofType2) { }


You can see that defaultExpr1 and defaultExpr2 have been duplicated.
2

There are 2 answers

2
parktomatomi On

You can pass the arguments to private members 1 & 2 with a protected constructor. This will keep those members shielded without giving the derived classes full access to them.

class Base
{
private:
   Base();
protected:
   Base(Type1 overrideOfType1, Type2 overrideOfType2) { }
private:
   Type1 m_ofType1;
   Type2 m_ofType2;
};

class Derived : public Base
{
public:
   Derived() : Base(Type1{}, Type2{}) { }

   Derived(Type1 overrideOfType1) : Base(overrideOfType1, Type2{}) { }

   Derived(Type2 overrideOfType2) : Base(Type1{}, overrideOfType2) { }
};

Oh, and by the way, members are initialized with the default constructor if they have one automatically, so you don't need to be explicit for the empty base constructor. For instance, if the default constructor of Base was accessible, you could just define:

Base::Base() = default;

And the members would have their default values.

0
463035818_is_not_an_ai On

I think the Derived is close to being a red herring. Basically you are asking for 3 ways to construct a Base and to do that it is Base that has to provide appropriate constructors, something along the line of:

struct Base {
   Type1 m_ofType1{defaultExpr1};
   Type2 m_ofType2{defaultExpr2};
   Base() = default;
   Base(Type1 x) : m_ofType1(x) {}
   Base(Type2 x) : m_ofType2(x) {}
};

Default initializers (defaultExpr1 and defaultExpr2) can be overriden by initializers in the constructors initializer list.

Now add access restrictions and derived classes calling those constructor as you wish (perhaps making the last two protected and data members private). To stay with your example that would be

 struct Derived : Base {
    Derived(Type1 x) : Base(x) {}
    Derived(Type2 x) : Base(x) {}
 };

I doubt you can get it any shorter than this.