Virtual but not multiple inheritance to invoke the grandparent constructor

I have code like this:

class Ref {<undefined>};
Ref refObjectForA, refObjectForB;

class Base
{
  public:
    Base(const Ref & iRef) : _ref(iRef) {}
    virtual ~Base() {}

    const Ref & ref;
};

class A: public Base
{
  public:
     A() : Base(refObjectForA) {}
     virtual ~A() {}
};

class B: public A
{
  public:
    B() : Base(refObjectForB) {} // won't compile: Base is not direct base of B
    virtual ~B() {}
};

      

As an attribute is a reference, I think I can only set it in the constructor, so I need to call the constructor Base

in B()

. I found two ways, providing a forward constructor in A

(but this implies adding code to all classes that can be inherited):

A(const Ref& iRef): Base(iRef)

      

or using virtual inheritance:

class A: public virtual Base

      

The second option allows for simpler code to implement B

, but I'm wondering if I'm overusing virtual inheritance in an ugly trick or if it's a valid utility.

  • Can I use virtual inheritance in this case?
  • If not, what is the reason?

One of the "unexpected" behaviors I found is that the pointer static_cast

a Base

for B

pointer to B

cannot be shown due to virtual inheritance.

Also, I'm also wondering why this works (I mean why a B().ref == refObjectForB

): I would have thought that an implicit call A()

to the default constructor in B()

would overwrite the attribute ref

after the explicit Base

constructor, but perhaps this is not the case with virtual inheritance.

+3


source to share


2 answers


The best option I can see if you want to stick to an inheritance hierarchy is to implement protected constructors that reference the class Base

. Creating a protected constructor ensures that the (final) instance cannot be constructed using that constructor, so it will only be used in subclasses to initialize superclasses.

With a more or less ugly and dangerous macro, it becomes easy to write:

#define REF_FORWARD_CTOR(ClassName, DirectSuperClassName) \
    protected: ClassName(class Ref &r) : DirectSuperClassName(r) {} \
    public:

class A : public Base
{
    REF_FORWARD_CTOR(A, Base)
public:
    A() : Base(refObjectForA) {} // normal ctor
};

class B : public A
{
    REF_FORWARD_CTOR(B, A)
public:
    B() : A(refObjectForB) {} // normal ctor
};

      

An alternative would be to allow A

and B

output (directly) from Base

. Then add functions using multiple inheritance and "generic classes", possibly private, depending on what they are for:



class Base {
};

class Common {
    // common stuff used by both A and B
};

class A : public Base, public Common {
    // no further stuff here
};

class B : public Base, public Common {
    // add more stuff, or put it in a common super-class again,
    // if some classes want to inherit from B again
};

      

The problem with this design is that functionality in and Common

out cannot access materials in A

and B

. To resolve this issue, do one of the following:

  • If only static stuff is required: CRTP specify A

    / B

    on a specific type Common

    : Common<A>

    can then use A::...

    , but has nothing to do with a specific instanceA

  • If an instance is required: provide a pointer / reference in the constructor Common

    (small overhead)
  • Combining the first two solutions: use CRTP, implement wrapper functions in A

    and B

    that call functions in Common<A>

    and Common<B>

    , providing this

    (which is A*

    or B*

    via an optional parameter.
  • Same as above, but the class Common

    may also not be templated (no CRTP), if you overload / create these functions in that pointer argument ("CRTP on functions" if you want to call it that), the code speaks louder than words ... (The sample code is unreferenced and focuses on the "generic class".)
+4


source


Yes, you can technically use virtual inheritance to achieve the goal of providing a reference in the derived class itself.

And yes, it is the smell of design.



Your classes don't need to know anything other than their immediate foundations (virtual inheritance is the exception to the rule when it's needed for other reasons).

+2


source







All Articles