C++中的惰性/多阶段构造

Dan*_*nra 7 c++ constructor boost lazy-initialization

在C++中,对于多阶段构造/初始化对象,有什么好的现有类/设计模式?

我有一个带有一些数据成员的类,应该在程序流程的不同点初始化,因此必须延迟它们的初始化.例如,可以从文件中读取一个参数,从网络中读取另一个参数.

目前我使用boost :: optional来延迟构建数据成员,但是让我感到烦恼的是,optional在语义上与delay-construct不同.

我需要提醒boost :: bind和lambda部分函数应用程序的功能,并且使用这些库我可以设计多阶段构造 - 但我更喜欢使用现有的测试类.(或许还有另一种我不熟悉的多阶段建筑模式).

j_r*_*ker 4

关键问题是是否应该在类型级别区分完全填充的对象和不完全填充的对象。如果您决定不做区分,那么只需使用boost::optional或与您正在做的类似:这使得您可以轻松快速地进行编码。OTOH,您无法让编译器强制执行特定函数需要完全填充的对象的要求;您每次都需要对字段执行运行时检查。

参数组类型

如果您确实在类型级别区分完全填充的对象和不完全填充的对象,则可以强制要求向函数传递完整的对象。为此,我建议XParams为每个相关类型创建一个相应的类型XXParams每个参数都有boost::optional成员和设置函数,可以在初始构造后设置。然后,您可以强制X只有一个(非复制)构造函数,该构造函数将 anXParams作为其唯一参数,并检查是否已在该XParams对象内设置每个必要的参数。(不确定这个模式是否有名称——有人喜欢编辑这个来填写我们的信息吗?)

“部分对象”类型

如果在对象完全填充之前您实际上不需要对对象执行任何操作(也许除了像取回字段值这样的琐碎事情之外),那么这会非常有效。如果您有时确实必须将不完全填充的数据X视为“完整” X,则可以改为从包含所有逻辑的X类型派生,以及用于执行先决条件测试的虚拟方法,以测试是否填充了所有必需的字段。然后,如果确保它只能在完全填充的状态下构造,它可以通过总是返回的简单检查来覆盖那些受保护的方法:XPartialprotectedXtrue

class XPartial {
    optional<string> name_;

public:
    void setName(string x) { name_.reset(x); }  // Can add getters and/or ctors
    string makeGreeting(string title) {
        if (checkMakeGreeting_()) {             // Is it safe?
            return string("Hello, ") + title + " " + *name_;
        } else {
            throw domain_error("ZOINKS");       // Or similar
        }
    }
    bool isComplete() const { return checkMakeGreeting_(); }  // All tests here

protected:
    virtual bool checkMakeGreeting_() const { return name_; }   // Populated?
};

class X : public XPartial {
    X();     // Forbid default-construction; or, you could supply a "full" ctor

public:
    explicit X(XPartial const& x) : XPartial(x) {  // Avoid implicit conversion
        if (!x.isComplete()) throw domain_error("ZOINKS");
    }

    X& operator=(XPartial const& x) {
        if (!x.isComplete()) throw domain_error("ZOINKS");
        return static_cast<X&>(XPartial::operator=(x));
    }

protected:
    virtual bool checkMakeGreeting_() { return true; }   // No checking needed!
};
Run Code Online (Sandbox Code Playgroud)

虽然这里的继承看起来是“从后到前”,但这样做意味着可以安全地在任何需要Xan 的地方提供an ,因此这种方法遵循里氏替换原则。这意味着函数可以使用 的参数类型来指示它需要一个完整的对象,或者指示它可以处理部分填充的对象——在这种情况下,可以传递一个对象或一个完整的对象。XPartial&X&XXPartial&XPartialX

最初我有isComplete()as protected,但发现这不起作用,因为X的复制构造函数和赋值运算符必须在其XPartial&参数上调用此函数,并且它们没有足够的访问权限。经过反思,公开公开此功能更有意义。