假设我有一个基类A
,两个派生类B
和C
,它们是virtual public A
, 和一个最终类 D
,它是 public B, public C
。我试图为此制作一个工作代码:
#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;
}
所有类通常都有自己的 .h
和 .cpp
文件,我将其压缩了一下,公式纯属虚构。简而言之,A
充当一个接口(interface)类,它具有 m_x
作为 B
和 C
的公共(public)变量>,根据需要使用和修改,而 D
使用 A
的变量进一步计算所需的 m_w
。这两个虚拟类是根据需要调用的,来自D
。
我的问题:如果没有给定默认参数,我无法编译上面的代码,但如果给定了,结果是错误的。
a) 原样(无默认参数),第一个错误行:
16:29 没有用于调用 A::A() 的匹配函数
b) 没有默认参数,将第 19 行更改为 A::A(m_z)
:
不能直接调用构造函数‘B::A’[-fpermissive]
(我认为这是有道理的,没有B::A
这样的东西)
c) 默认参数:A=2, B=3, C=4,输出:
A()
A()
B()
A()
C()
A()
A()
B()
D()
10
这看起来很糟糕(我不知道为什么有这么多电话),但它也应该是:
B(5)
=>
m_y = 5 * 5 = 25
m_z = 2 * 25 + 1 = 51
A(51)
=>
m_x = 2 * 51 = 102
最后:
m_w = 102 * 5 = 510
……不应该吗?有人可以告诉我我做错了什么吗?
最佳答案
您需要在初始化列表中调用基类构造函数,而不是在构造函数体中。
替换
B(int a): m_y {a * a}
{
m_z = 2 * m_y + 1;
A(m_z);
std::cout << "B()" << '\n';
}
通过
B(int a): A(2 * a * a + 1), m_y {a * a}
{
m_z = 2 * m_y + 1;
std::cout << "B()" << '\n';
}
等等。
在 D
类中,您需要为 A
、B
和 C
调用构造函数,因为 C++ 不会不允许第二次猜测,当 D
创建时,A
构造函数是否会从 B
或 C
调用。
拍摄 2
由于您实际上希望 D
有条件地构造 B
或 C
的实例,因此您需要重新组织您的设计。
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).
所以你有不同的温度测量单位,你想用同一个测量单位一起显示它们。为了方便起见,我说目标单位是°C。
让我们设计一个温度类和一些助手和工厂方法
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)); }
现在,如果您有不同的国家并希望为每个国家的项目注册温度,您可以按如下方式定义国家(注意:还有许多其他方式)
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;
}
在显示/屏幕/图表类中,您可以保存一组 CountryBase
引用并访问它们的标准化为 °C 的温度,无论它们是如何构建的。
关于如果没有默认参数,C++ 派生类将无法工作,即使这样也是错误的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39016944/