【问题标题】:Initialization of sub-classes in main constructors - body of main constructor versus sub-class constructor body主构造函数中子类的初始化——主构造函数体与子类构造函数体
【发布时间】:2015-08-27 07:09:46
【问题描述】:

我的遗留代码有一个包含许多其他子类作为数据成员的类。其中大多数将各种输入读入子类中的数据成员。它看起来像这样:

MainClass::MainClass(std::string &directory, LogClass &logClass, int mode1, int mode2, int mode3,) : 
    logClass(logClass),                                                                                           
    subClass1(directory, logClass, mode1),                                                  
    subClass2(directory, logClass),                                                                                                                                      
    subClass3(logClass, subClass1, subClass2, mode1, mode2),
    subClass4(logClass, subClass1, subClass2, subClass3, mode1, mode2, mode3),
{
    if(mode2 == 0)                         
    {
        subClass3.init();
    }
    subClass4.init();
}

在某些子类中,初始化发生在其构造函数的主体中。在其他时候,重要数据成员的初始化发生在 mainClass 的主体中,如 subClass3 和 subClass4 的情况。

我正在更改此代码以包含处理先前读入数据的转换的其他子类。其中一些新类需要在构造它们之前初始化其他类。例如,如果我添加 subClass5,它可能需要运行 subClass4.init()。但是从代码中可以看出, subClass4.init() 直到构造函数的主体才运行。

这里有什么好的政策可以继续前进?我是否应该将所有初始化从子类构造函数中提取到它们自己的 init() 函数中,并在主类构造函数中正确排序它们?或者我应该将所有初始化移动到子类构造函数和托盘中并正确排序?

我正在尝试找出最适合此问题的代码设计。

【问题讨论】:

  • 如果示例反映了遗留代码,我会考虑对其进行重构。每当我看到“模式”参数时,我都觉得应该通过子分类来完成,或者——最好是——使用策略或类似的模式。

标签: c++ class constructor


【解决方案1】:

这取决于init() 函数的语义。他们在做什么?他们的构造函数是做什么的?为什么要使用两个不同的函数(构造函数和init)进行初始化?

在我见过的很多代码中,通常inits 函数主要是由于它们的实现者的草率。从 C++ 的角度来看,他们所做的属于构造函数。

另一方面,如果 init()s 函数用于执行与对象构造语义不相交的事情(例如,启动计时器、触发某些事件……),也许它很好,它们与构造函数不相交。不过,在这种情况下,我会更改他们的名字。

【讨论】:

    【解决方案2】:

    构造函数应该总是建立类的不变量,不多也不少。我强烈认为,在构造后处于无效状态并且必须使用 init() 调用初始化的类是格式错误的。

    出于测试目的,我会考虑将子部分的构造移出类并将它们提供给构造函数,或者为每个子部分提供一个工厂。这使您能够为子部分创建模拟,并且对单元测试非常有帮助。

    【讨论】:

      【解决方案3】:

      init() 这样的方法有助于将初始化过程委托给以后的时间,从而允许快速启动,因为构造函数不必花费太多时间来执行init()。如果那些 init 方法正在执行昂贵的操作,例如使用密集的 CPU 周期或获取昂贵的资源,则尤其如此。

      另一方面,如果您实施这样的协议,那么任何添加subClassX 的人都必须通过调用子类构造函数并随后在这些子类上调用init 来确保正确初始化。

      这两种方法各有利弊,完全由您自己选择。如果你发现优点多于缺点,那就去吧!

      附:当然,这假设构造函数已经足够让子类实例进入最小可用状态。

      【讨论】:

      • 如果在使用对象之前需要调用init,它会创建一个不变量不成立的无效对象。出于性能原因,您可以在需要时实例化对象并让构造函数运行,或者使用 (unique_) 指针作为成员并懒惰地创建它。
      • 什么是最小可用状态?如果一个对象的初始化很耗时,你可能想在另一个线程上执行它,但是资源获取是初始化...
      • 我看到了您独特的指针方法和延迟初始化,但是如果构造函数可以在启动时让事情继续进行,那么委托一个昂贵的init 通常是广泛使用的想法。例如:GUI 对象渴望获取一些位图。这可以有效地委派给以后的时间点。
      • @Arun 我认为我们有一些误解,至少在我这边。当看到一个 init 方法时,我通常将其解释为“在我可以对对象做任何事情之前,我必须调用 init”。这是我要防止的。在您的 GUI 示例中,该对象完全有效并且可以使用,例如加载位图。可能与 init 不同,例如loadVisuals() 会更好。
      • @Paolo M,最小可用状态,我的意思是仅仅为这些对象提供足够的身份以供其他子系统识别它们,但是,也许只有在 init 之后才可用 他们
      猜你喜欢
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多