Reputation: 849
Suppose I have a base class A
, two derived classes B
and C
which are virtual public A
, and a final class D
, which is public B, public C
. I tried to make a working code for this:
#include <iostream>
class A
{
protected:
int m_x;
public:
A(int a): m_x {2 * a} { std::cout << "A()" << '\n'; }
};
class B : virtual public A
{
private:
int m_y, m_z;
public:
B(int a): m_y {a * a}
{
m_z = 2 * m_y + 1;
A(m_z);
std::cout << "B()" << '\n';
}
int getVal() { return m_y; }
};
class C : virtual public A
{
private:
int m_p, m_q;
public:
C(int a): m_p {1 - a}
{
m_q = m_p - 3 * a;
A(m_q);
std::cout << "C()" << '\n';
}
int getVal() { return m_p; }
};
class D : public B, public C
{
private:
int m_w;
public:
D(int a, int b)
{
switch (b)
{
case 1: { B(a); break; }
case 2: { C(a); break; }
}
m_w = m_x * a;
std::cout << "D()" << '\n';
}
int getVal() { return m_z; }
};
int main(int argc, char *argv[])
{
D d {5, 1};
std::cout << d.getVal() << '\n';
return 0;
}
All classes normally have their own .h
and .cpp
file, I compacted it a bit and the formulas are pure fiction. The short story is that A
acts as an interface class who has m_x
as a common variable for both B
and C
, to be used and modified as needed, while D
uses A
's variable to further calculate the desired m_w
. The two virtual classes are called on a need basis, from D
.
My problem: I can't compile the above if no default parameters are given, but if they are, the results are wrong.
a) As it is (no default parameters), first error line:
16:29 no matching function for call to A::A()
b) No default parameters, change line 19 to A::A(m_z)
:
cannot call constructor ‘B::A’ directly [-fpermissive]
(I think this makes sense, no such thing as B::A
)
c) With default parameters: A=2, B=3, C=4, outputs:
A()
A()
B()
A()
C()
A()
A()
B()
D()
10
which looks awful enough (I don't know why so many calls), but it should also be:
B(5)
=>
m_y = 5 * 5 = 25
m_z = 2 * 25 + 1 = 51
A(51)
=>
m_x = 2 * 51 = 102
finally:
m_w = 102 * 5 = 510
...shouldn't it? Can someone please tell me what I am doing wrong?
Upvotes: 0
Views: 72
Reputation: 13458
You need to call base class constructors in the initializer list, not in the constructor body.
Replace
B(int a): m_y {a * a}
{
m_z = 2 * m_y + 1;
A(m_z);
std::cout << "B()" << '\n';
}
By
B(int a): A(2 * a * a + 1), m_y {a * a}
{
m_z = 2 * m_y + 1;
std::cout << "B()" << '\n';
}
And so on.
In class D
you need to call constructors for A
, B
and C
, since C++ doesn't allow second guessing, whether the A
constructor would be called from B
or C
when D
is created.
Take 2
Since you actually want D
to conditionally construct an instance of B
or C
, you need to reorganize your design.
Suppose I have temperature readings (A) from different countries (B, C, etc), and I have to filter/present/display them on screen/graph/somehow (D) (just an example).
So you have different measurement units of temperatures and you want to display them together in the same measurement unit. For my personal convenience, I say that the target unit is °C.
Lets design a temperature class and some helpers and factory methods
int CelsiusToFahrenheit(int c)
{ return /* somehow calculate F from c */; }
int FahrenheitToCelsius(int f)
{ return /* somehow calculate °C from f */; }
class Temperature
{
protected:
int m_temperatureCelsius;
public:
Temperature(int temperatureCelsius): m_temperatureCelsius { temperatureCelsius }
{ std::cout << "A()" << '\n'; }
int GetCelsius()
{ return m_temperatureCelsius; }
int GetFahrenheit()
{ return CelsiusToFahrenheit(m_temperatureCelsius); }
};
Temperature* CreateTemperatureFromCelsius(int c)
{ return new Temperature(c); }
Temperature* CreateTemperatureFromFahrenheit(int f)
{ return new Temperature(FahrenheitToCelsius(f)); }
Now if you have different countries and want to register a temperature per country item, you could define countries as follows (note: there are MANY other ways)
class BaseCountry
{
protected:
Temperature* m_temperature;
public:
BaseCountry()
{ }
virtual ~BaseCountry() = 0; // abstract class
int GetCountryTemperatureCelsius()
{ return m_temperature ? m_temperature->GetCelsius() : 0; }
}
class CountryA : public BaseCountry
{
public:
CountryA(int temperatureC)
{ m_temperature = CreateTemperatureFromCelsius(temperatureC); }
~CountryA()
{ delete m_temperature; }
}
class CountryB : public BaseCountry
{
public:
CountryB(int temperatureF)
{ m_temperature = CreateTemperatureFromFahrenheit(temperatureF); }
~CountryB()
{ delete m_temperature; }
}
int main(int argc, char *argv[])
{
BaseCountry& c1 = CountryA(10);
BaseCountry& c2 = CountryB(10);
// display 10
std::cout << c1.GetCountryTemperatureCelsius() << '\n';
// display the number in °C for 10 F
std::cout << c2.GetCountryTemperatureCelsius() << '\n';
return 0;
}
In a display/screen/diagram class, you can hold an array of CountryBase
references and access their temperature normalized to °C, no matter how they where constructed.
Upvotes: 4