【问题标题】:Initializing variables in a class hierarchy在类层次结构中初始化变量
【发布时间】:2014-09-07 07:40:24
【问题描述】:

我被分配了清理类层次结构的任务,但我有点墨守成规。我现在留下的东西大致如下:

class Base
{
Base() = 0;

function1();
   ...
function5();

protected:

int variable1_;
   ...
int variable10_;
}; 

class DerivedOne
   : public Base
{
   DerivedOne() = 0;

   function6();
      ...
   function10();

 protected:

   int variable11_;
      ...
   int variable20_;
};

class DerivedTwo
   : public DerivedOne
{
    DerivedTwo()
       : DerivedOne() 
    {
       ... Fill variable1_ to variable25_ with data read from file ...
       variable1_ = ...
    }

    function11();
      ...
    function15();

  private:

    int variable21_;
      ...
    int variable25_;
};

所以在 DerivedTwo 中为 25 个变量赋值,容易出错。当另一个类从 DerivedOne 继承时,几乎可以肯定该人会忘记初始化其中一个变量。

我一直在尝试不同的方法来设计这个层次结构,但没有什么感觉是对的。

我知道如果不知道这些类的实际作用就很难说出具体的内容,但是我很想知道这个设计是否存在根本性的错误,或者我是否忽略了一些初始化所有变量的优雅方法。

【问题讨论】:

  • 为什么不使用特定的非默认构造函数(例如传递文件名、共享句柄或其他)初始化这些类之一中的变量?有什么理由不这样做?
  • 从 DerivedOne 继承的其他类不会初始化文件中的值,因此 Base 类应该更通用。抱歉,我应该在帖子中提到这一点。
  • 你能贴出真实的代码吗? Base() = 0 是什么? DerivedTwo() : DerivedOne() 是什么?你知道DerivedOne() 是自动调用的吗?
  • 是否需要在构造函数中读取文件?如果读取出错或无法打开文件怎么办:返回一个空对象?还是不一致?

标签: c++ class oop inheritance design-patterns


【解决方案1】:

派生的原则是先构造基础对象,然后派生。这需要 Base 和 DerivedOne 构造函数自行生成有效对象。

在构造 DerivedTwo 时,您的代码说明了这一点:

   DerivedTwo()
       : DerivedOne()  // First the base object constructor is called, before anything else 
   {
       // then remaining initialisation, after DerivedOne() was created
   }

因此,每个类至少应将其自己的变量初始化为其自己的函数可以以一致的方式处理的初始有效状态。

Base() : variable1_(/*default value here*/), variable10_(/*default value here*/) {}
Base(int v1, int v10) : variable1_(v1), variable10_(v10) {} // for convenience

一个派生类之后可以覆盖其基类的变量。当然,使用 getter/setter 会更安全、更优雅,但没关系。

如果您采用这种设计,如果一个子派生类忘记初始化其父类之一的变量,这不应该导致灾难,因为至少基础对象以一种体面的方式初始化。

现在的问题是您还通过读取文件来启动 DerivedTwo。请记住,首先创建 Base,然后创建 DerivedOne,然后只有 DerivedTwo 读取值。当文件无法读取或不一致时会发生什么?除非您抛出异常,否则您最终可能会得到一个不一致的对象。所以你必须管理它并确保 DerivedTwo 处于稳定状态:

   DerivedTwo()
       : DerivedOne()  // First the base object constructor is called 
   {
       // then initialise all own members to a consitent state
       // then try to read the file and manage potential errors 
   }

【讨论】:

  • 非常感谢,我会在重新设计时记住所有这些。
【解决方案2】:

每个类都应该负责初始化自己的数据成员。正如您所指出的,Base 假设DerivedX 将初始化其数据成员是危险的。所以...

Base 应该初始化 variable1_ ... variable10_

DerivedOne 应该初始化 variable11_ ... variable20_

DerivedTwo 应该初始化 variable21_ ... variable25_

...等等。

当然,这并没有解决为什么类以这种方式布局的问题......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 2015-10-10
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多