【问题标题】:Constructor that depends on object not yet constructed依赖于尚未构造的对象的构造函数
【发布时间】:2012-07-05 11:48:19
【问题描述】:

在 C++ 中,我有一个对象 A,它有一个接受 istream 的构造函数(从文件中加载自身)。我有另一个类有A 作为成员。我无法从初始化列表中调用A 的构造函数,因为我还没有打开istream。一旦我在我的类的构造函数中打开它,调用A 的构造函数就来不及了。有没有办法将初始化列表中的istream 打开到某个临时对象中,以便我可以将它发送到A 的构造函数?
如果有,在调用初始化列表的顺序上是否有任何类型的保证,以便istreamA 之前被初始化?

一个例子可能会有所帮助:

class A {
public:
  A(std::istream const&);
}

class B {
public:
  B(std::istream const&);
}

class MyClass {
  A a;
  B b;
public:
  MyClass() : a(is), b(is) {  // <-- How to do this?
    std::istream is("path");
  }
}

【问题讨论】:

  • 听起来dependency injection 可能是一个可能的解决方案。这也可能是一个有趣的阅读:misko.hevery.com/code-reviewers-guide/….
  • @baruch 动态创建ab 有什么问题初始化istream之后?
  • @EitanT istream 仅在构造之前/期间需要。一旦构造函数开始运行,然后在整个使用MyClass,我对这个istream没有用处
  • @EitanT 是成员,而不是指针。我能获得的自动内存管理越多越好。
  • @baruch 当然,但你必须接受一些事情。

标签: c++ oop constructor


【解决方案1】:

还有一个想法...如果您想避免动态分配并使用单个临时的istream,您可以将AB 的加载阶段从它们的构造函数中移开:

class A {
public:
  Load(std::istream const&);
}

class B {
public:
  Load(std::istream const&);
}

class MyClass {
  A a;
  B b;
public:
  MyClass() {
    std::istream is("path");
    a.Load(is);
    b.Load(is);
  }
}

顺便说一句,这需要 AB 从某个共享父级继承加载能力,但这显然超出了这个问题的范围。

【讨论】:

  • 为什么AB 需要从某个共享父级继承?我使用了这个,但是跳过了继承的事情,看不出有什么原因。
  • @baruch,如果AB 都有一个几乎相同的Load(std::istream const&amp;) 方法,那么您就有代码重复。使用继承可以消除这种重复。由于您不需要继承来实现多态性,因此您甚至可以将其设为非公开。或者,您可以将调用委托给某个全局函数或第三类。只是没有执行加载的代码的两份副本。
  • 哦。我理解你的意思,但这不是我的情况。我有一个系统MyClass,它由两个独立的系统AB 组成。 MyClass 需要能够将其状态保存到文件中,然后在下次运行时重新加载其状态。两个子系统的Load 完全不同(一个系统是字符串索引,另一个是整数树)
【解决方案2】:

改为使用指向对象的指针:

class MyClass {
   A *a;
   B *b;
public:
   MyClass()
   {
       std::istream is("path");
       a = new A(is);
       b = new B(is);
   }
}

别忘了释放析构函数中的内存。

【讨论】:

    【解决方案3】:

    既然两个成员都应该依赖同一个 istream,为什么不呢:

    class A {
    public:
      A(std::istream const&);
    }
    
    class B {
    public:
      B(std::istream const&);
    }
    
    class MyClass {
      A a;
      B b;
    public:
      MyClass(std::istream const& is) : a(is), b(is) {
      }
    }
    

    【讨论】:

    • is的初始化在哪里?课外?
    • 当然,我什至更喜欢这样,因为它没有路径那么具体。但我也想知道为什么 A 和 B 应该生活在同一个实例上。
    【解决方案4】:

    也许我遗漏了什么,但有什么问题:

    class MyClass {
        A* a;
        B* b;
    public:
        MyClass() {
          std::istream is("path");
          a = new A(is);
          b = new B(is);
        }
    
        ~MyClass() {
            delete a;
            delete b;
        }
    }
    

    【讨论】:

    • 呸!除此之外,没什么,只是我在徘徊,如果有办法以我的“更清洁”风格做到这一点。
    【解决方案5】:

    类的成员变量的初始化将按照您在类声明中指定的顺序进行。

    请注意,当初始化列表中的顺序与成员变量的声明顺序不对应时,gcc 将发出警告(给定 -Wall)。类中的声明顺序是权威的。

    A类{ 民众: A(std::istream const&); } B类{ 民众: B(std::istream const&); } 我的班级{ std::istream 是; 一个; 乙乙; 民众: MyClass() : is("path"), a(is), b(is) { } }

    【讨论】:

    • 我喜欢这个,除了成员is在整个使用类的过程中没有任何用处
    • 我还要在构造函数的开头添加一个is.close()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-19
    • 2019-03-24
    • 2011-02-02
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多