gpt4 book ai didi

C++ 在父类(super class)构造函数中运行依赖于子类覆盖的大量变量的代码的正确方法是什么?

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:46:44 27 4
gpt4 key购买 nike

假设我有一个父类(super class),当它初始化时,想要运行一些依赖于一大堆类变量的代码,这些类变量可能会或可能不会被其构造函数中的子类覆盖。

什么是可接受的、干净的编码方式?

我觉得我脑子放屁了;这应该是继承的标准初学者用法,但我想不通。

例如假设我有一个代表车辆的父类(super class),当它启动时,我想在它处理的地方做一大堆代码,比如,每轴的负载或其他东西(无关紧要),但该代码用作输入一堆所有车辆都存在的参数(因此存在于父类(super class)中),例如重量、长度、numwheels、numaxles,甚至可能是定义每个轴有多少个车轮等的复杂数据结构。

各种子类(sportscar、bigrig、motorcycle)想要在父类(super class)进行处理之前设置重量、长度、numwheels、nummaxles 等。


Super::Super() {
Process(var1_,var2_,var3_,var4_, ...);
}

Sub1::Sub1(): Super() {
var1_ = <some math>;
var2_ = <some math>;
...
}

不起作用,因为父类(super class) Process() 在子类设置变量之前运行。对吧?


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() 很奇怪。如果另一个程序员想从我的父类(super class)继承子类并且不知道我的魔法规则怎么办?


正确的做法是什么?

最佳答案

有很多方法可以解决这个问题(C++ 中的 lack of virtual constructors)。每一个都有自己的优点和缺点。解决此限制的最常见模式:

  • 将所有必需的参数传递给基类构造函数。如果您需要多个参数,这真的很烦人。如果需求发生变化,代码的可读性将越来越差,并且很难扩展。当然,它有一个很大的好处:它不是解决方法,每个人都会理解。

  • 更改您的设计(这可能是最好的做法,但可能需要大量工作)。如果您需要很多参数,那么您可以将所有参数打包到单独的类中,它将保存对象状态。基类构造函数将只接受一个这种类型的参数,并且它将包含它的状态(或者只是它的初始化数据,但这是另一回事)。它的好处是保持设计清晰(没有像第一个解决方案那样的解决方法)但如果此初始化 token 将随着其自己的类层次结构发展,它可能会涉及一些复杂性。

  • 添加公共(public)初始化方法。将您的 Init() 方法更改为 public,它不会被派生构造函数调用,而是被类用户调用。这将允许您在每个派生类中添加初始化代码(然后初始化顺序由实现本身控制)。这种方法很老派,它需要用户调用它,但它有一个很大的好处:它是众所周知的,不会让任何人感到惊讶。参见 this post here on SO进行有关它们的小型讨论。

  • 虚拟构造函数习语。参见 this article供引用。它按预期工作,您可以使用一些模板方法使其更易于使用。 IMO 最大的缺点是当你创建一个新的派生类时,它改变了你管理继承和初始化的方式。这可能无聊并且容易出错和冗长。此外,您还更改了类的实例化方式,对我来说,这总是很烦人。

关于第二种解决方案的一些注释(来自评论)。如果你应用它,我至少会看到这些选项:

  • 包含所有必需参数的愚蠢实体(只有数据,没有逻辑)。

  • 将对象状态封装在一个单独的对象中。您从派生类传递的对象不会被使用和删除,但它将成为对象的一部分。

在这两种情况下,您可以为参数(BaseParametersHolderDerivedParametersHolder 等)设置或不设置并行层次结构。请注意 holder 不会遇到与第一个解决方案相同的问题(许多参数),因为创建可以委托(delegate)给私有(private)函数(示例是为了说明概念,代码远不是nice):

class Derived : public Base 
{
public:
Derived() : Base(CreateParameters())
{
}

private:
ParameterHolder CreateParameters()
{
ParameterHolder parameters;
parameters.Value = 1;
parameters.AnotherValue = 2;

return parameters;
}
};

用什么?没有答案。我更愿意跨代码保持一致(因此,如果您决定使用 holders 然后在任何地方使用它们,请不要混合 - 例如 - 与 v.i. idiom)。每次只选择合适的一个,并尽量保持一致。

关于C++ 在父类(super class)构造函数中运行依赖于子类覆盖的大量变量的代码的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21108836/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com