I am using C 11. I am trying to declare 2 interfaces: B and C, which each declare some functions to be implemented by the child classes. Both interfaces rely on variables and functions which are declared in the common A class. Even this relatively simple structure leads to the diamond heritance problem.(https://www.makeuseof.com/what-is-diamond-problem-in-cpp/) I use virtual inheritance to link A and B/C, trying to implement something like this:
#edit 1 Modified the original code snippet to be a minimal reproducable example.
class T1{};
class A{
public:
A(const T1& param1);
void myfuna();
const T1 member1;
};
class B : public virtual A{
virtual void myfunb()=0;
};
class C: public virtual A{
virtual void myfunc()=0;
};
class Di: public B, public C{
Di(const T1& param1): A(param1){}
void myfunb() override;
void myfunc() override;
};
However, this code won't compile. The reason is that cpp interfaces aren't exactly speaking "interfaces" (I have a Java background), as they still have a default constructor which is called, even when virtual inheritance is used. This means that upon initialization of an instance of "D", these constructors are called:
- A(param1,param2)
- B()
- C()
The B and C constructors in turn try to call the A() constructor, which is ill-defined (I get the "call to implicitly-delete default constructor" error). The culprit is the const field of A, which disables the default constructor.
I can't find an elegant way to remedy this problem. Am I missing something, is my design flawed or is this just a limitation of cpp?
CodePudding user response:
The solutions are:
- Make
B
andC
abstract classes. - Or define a default constructor for
A
. - Or call the non-default constructor of
A
in the constructors ofB
andC
.
CodePudding user response:
As stated by @eerorika, there are 2 options to solve this issue:
Define a default constructor for A()
It is relatively easy to instantiate the parent class with a dummy parameters, so that a default constructor is defined. It is a bit ugly, but because the object created by the default constructor will normally never be used, it should work.
I still have one remark: Imagine class B implements one of his functions (see "implementeB") and needs to access member1. What object will be accessed? The dummy variable or the variable given to the constructor of Di (param1)? If the dummy variable is used, this is a problem.
class T1{};
static const T1 dummy;
class A{
public:
A(): member1(dummy){}
A(const T1& param1);
void myfuna();
const T1 member1;
};
class B : public virtual A{
public:
virtual void myfunb()=0;
T1 implementedB(){
return member1;
}
};
class C: public virtual A{
public:
virtual void myfunc()=0;
};
class Di: public B,
public C
{
public:
Di(const T1& param1):
A(param1){}
void myfunb() override{}
void myfunc() override{}
};
Call the non-default constructor of A in the constructors of B and C. I believe this is the correct solution. However, it also means that the argument list of A needs to be propagated to all the subclasses. For bigger classes, this can quickly grow quite big, and it kind of defeats the purpose of an "interface". It also means that the child classes ("Di") need to explicitly call all base class constructors (A(..), B(..) and C(..))I guess there isn't an ideal way...
class T1{};
static const T1 dummy;
class A{
public:
A(const T1& param1);
void myfuna();
const T1 member1;
};
class B : public virtual A{
public:
B(T1 param1) : A(param1){}
virtual void myfunb()=0;
};
class C: public virtual A{
public:
C(T1 param1) : A(param1){}
virtual void myfunc()=0;
};
class Di: public B,
public C
{
public:
Di(const T1& param1):
A(param1), B(param1), C(param1){
}
void myfunb() override{}
void myfunc() override{}
};