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

标签 c++ boost constructor lazy-initialization

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

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

目前我正在使用 boost::optional 来延迟构造数据成员,但令我困扰的是,optional 在语义上与延迟构造的不同。

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

最佳答案

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

参数组类型

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

“部分对象”类型

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

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!
};

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

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

关于c++ - C++ 中的惰性/多阶段构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2153150/

相关文章:

c++ - 什么时候调用构造函数?

c++ - 为什么 QXmlQuery 似乎在结果中添加了 `\n`? (以及如何解决?)

c++ - 在 C++ 中使用 Boost-Python 访问 Python 中定义的函数数组

c++ - 采用 const 参数的默认移动构造函数

c++ - Linux初学者,boost库放在哪里?

c++ - CMake 错误查找 Boost header

c++ - 构造函数初始化列表中使用的变量顺序重要吗?

c++ - 从服务器发送推送通知

c++ - 对宽度不匹配的输出缓冲区进行矢量化

c++ - 如何在使用 TBB 时检查当前有多少线程正在运行?