假设我有一个超类,当它初始化时,想要运行一些依赖于一大堆类变量的代码,这些类变量可能会或者可能不会被构造函数中的子类覆盖。
我觉得我有一个大脑放屁;这应该是继承的标准初学者用法,但我无法弄清楚。
e.g。说我有一个代表车辆的超类,当它开始时,我想做一大堆代码来处理,比如每轴的负载或什么(无关紧要),但该代码用作输入一堆所有车辆存在的参数(因此存在于超类中),比如重量,长度,轮数,核心数,甚至可能是定义每个车轮轮数的复杂数据结构等。)
各种子类(sportscar,bigrig,motorcycle),想要在超类进行处理之前设置权重,长度,轮数,numax等。
Super::Super() {
Process(var1_,var2_,var3_,var4_, ...);
}
Sub1::Sub1(): Super() {
var1_ = <some math>;
var2_ = <some math>;
...
}
不起作用,因为超类Process()在子类设置vars之前运行。正确?
Super::Super(float var1, WackyDatastructureDef var2, int var3, WackyStruct2 var4, ...),
var1_(var1), var2_(var2), var3_(var3), ............... {
Process(var1_,var2_,var3_,var4_, ...);
}
Sub1::Sub1(): Super(<some math>, <some math>, <some math>, <some math>, ......) {
....
}
由于显而易见的原因,它看起来很可怕。此外,如果我只需要覆盖20个默认变量值中的2个,那看起来很痛苦。
Super::Super() {}
void Super::Init() {
Process(var1_, var2_, var3_, var4_ ...... );
}
Sub1::Sub1(): Super() {
var1_ = <some math>;
var2_ = <some math>;
...
Init();
}
看起来最干净但我不喜欢它...我必须记住在所有子类构造函数的末尾调用Init()是很奇怪的。如果另一个程序员想要继承我的超类并且不知道我的魔法规则怎么办?
这样做的正确方法是什么?
答案 0 :(得分:2)
有很多方法可以解决这个问题(C ++中的lack of virtual constructors)。每个人都有自己的利弊。最常见的模式可以解决此限制:
将所有必需的参数传递给基类构造函数。如果您需要的参数不多,这可能会非常烦人。如果需求发生变化,代码将越来越不可读,并且很难扩展。当然它有一个很大的好处:它不是一种解决方法,每个人都会理解它。
更改您的设计(这可能是最好的选择,但可能需要大量工作)。如果您需要大量参数,那么您可以将所有参数打包在单独的类中,它将保持对象状态。基类构造函数将只接受这种类型的一个参数,它将包含其状态(或仅包含其初始化数据,但这是另一个故事)。它的好处是保持设计清晰(没有像第一个解决方案那样的解决方法)但如果这个初始化令牌将随其自己的类层次结构发展,它可能会涉及一些复杂性。
添加公开初始化方法。将您的Init()
方法更改为public,它不会被派生构造函数调用,而是由类用户调用。这将允许您在每个派生类中添加初始化代码(然后初始化顺序由实现本身控制)。这种方法非常旧学校,它要求用户调用它,但它有一个很大的好处:它是众所周知的,它不会让任何人感到惊讶。有关它们的简短讨论,请参阅this post here on SO。
虚拟构造函数。请参阅this article以获取参考。它按预期工作,您可以使用少量模板方法更容易使用。 IMO最大的缺点是它会在您创建新派生类时更改管理继承和初始化的方式。这可能无聊且容易出错和延迟。此外,你也改变了类的实例化方式,对我而言,这总是很烦人。
关于第二种解决方案的几点说明(来自评论)。如果你应用这个,我至少看到这些选项:
包含所有必需参数的愚蠢实体(只是数据,没有逻辑)。
将对象状态封装在单独的对象中。您从派生类传递的对象不会被使用和删除,但它将成为对象的一部分。
在这两种情况下,您可以拥有或不拥有参数的并行层次结构(BaseParametersHolder
,DerivedParametersHolder
等等)。请注意,持有者不会遇到第一个解决方案(许多参数)的相同问题,因为创建可以委托给私有函数(例子是说明概念,代码远远不是很好):< / p>
class Derived : public Base
{
public:
Derived() : Base(CreateParameters())
{
}
private:
ParameterHolder CreateParameters()
{
ParameterHolder parameters;
parameters.Value = 1;
parameters.AnotherValue = 2;
return parameters;
}
};
用什么?没有答案。我希望在代码之间保持一致(所以如果你决定使用持有者,那么到处使用它们,不要混用 - 例如 - 与v.i.idiom)。只需每次选择合适的一个并尝试保持一致。
答案 1 :(得分:0)
将相关信息传递给基类构造函数。