我应该使用Initialize-on-demand习语,如果是这样的话?

时间:2015-04-22 07:38:29

标签: c++ idioms idiomatic

我有以下代码:

 MyType x = do_something_dangerous();
 // ...
 if (some_condition) {
     // ...
     bar(x);
 }
 else { 
     // ...
 }
 // ...
 if (another_condition_which_may_depend_on_previous_if_else} {
     // ...
     baz(x);
 }

这个想法是,在某些情况下,提前确定可能很困难/不方便,我需要使用x。但是在我不需要使用它的情况下,尝试初始化它可能是不好的(比如说,可能会导致我的进程崩溃)。

现在,似乎我需要使用的是initialize-on-demand holder(链接侧重于Java,所以这里是草图):某种Wrapper<MyType>或{{1}使用Wrapper<MyType, do_something_dangerous>方法,以便第一个get()调用get()和更晚do_something_dangerous()只传递第一个调用获得的值。

  • 这确实是一种合适的方法吗?
  • 这个成语或其变体是否有一些标准(ish)实现?

备注:

  • 我可以使用get(),但这会有点麻烦,也会扭曲预期用途:&#34;建议在只有一个明确的情况下使用boost::optional(对于没有optional<T>类型值的原因,以及缺乏价值与T的常规值一样自然的原因。&#34;

2 个答案:

答案 0 :(得分:5)

恕我直言,你提出的解决方案非常合适:

  • 实施
  • 是微不足道的
  • 完全满足您的需求

当然,如果你已经在你的应用程序中使用了boost,你可以查看建议的boost :: optional模块,但是我不确定它是否正是你想要的,因为它更适用于可为空的对象需要是推迟初始化。

我的建议是:坚持专用的包装器实现。

与其他答案不同,我认为你应该使用单例,而只是像(使用初始化参数的实现):

template<typename T, typename I>
class LazyInit {
    T* val;
    I init;

public:
    LazyInit<T, I>(I init): init(init) {
        val = NULL;
    }
    ~LazyInit<T, I>() {
        delete val;
        val = NULL; // normally useless here
    }
    T& get() {
        if (val == NULL) {
            val = new T(init);
        }
        return *val;
    }
};

这是一个使用初始化函数的实现:

template<typename T>
class LazyInit {
    T* val;
    T (*init)();

public:
    LazyInit<T>(T (*init)()): init(init) {
        val = NULL;
    }
    ~LazyInit<T>() {
        delete val;
        val = NULL; // normally useless here
    }
    T& get() {
        if (val == NULL) {
            val = new T(init());
        }
        return *val;
    }
};
...
LazyInit<MyType> x(do_something);
...
bar(x.get()); // initialization done only here

您可以轻松地将两者结合起来,使用带参数的函数构建实现。

答案 1 :(得分:2)

如果你有c ++ 11,你可以使用std::asyncstd::shared_future的组合:

#include <iostream>
#include <future>

using namespace std;

struct LazyObj {
    LazyObj() {
        cout << "LazyObj init" << endl;
    }
    void test() {
        cout << "LazyObj test" << endl;
    }
};

int main() {
    auto x = std::async(launch::deferred, []() -> LazyObj& {
        static LazyObj a;
        return a;
    }).share();

    int cond=0;
    if( cond == 0 ) {
        x.get().test();
        x.get().test();
    }
    return 0;
}

在此用例launch::deferredstd::async不要创建执行线程,并在调用get()时按需调用lambda。要允许多次调用get()方法,我们会使用std::future方法将std::shared_future转换为share()。现在我们可以在需要的时间或地点获得lazy-initialisaion对象。