我在这个主题上稍微玩了一下,并提出了一个备选方案,以防您使用C++11。请考虑以下内容:
class MyClass
{
public:
MyClass() :
expensiveObjectLazyAccess()
{
expensiveObjectLazyAccess = [this]()
{
auto result = std::shared_ptr<ExpensiveType>(CreateExpensiveObject());
auto self = this;
expensiveObjectLazyAccess = [result]() { return result.get(); };
return self->GetExpensiveObject();
};
}
ExpensiveType* GetExpensiveObject() const
{
return expensiveObjectLazyAccess();
}
private:
std::function<ExpensiveType*()> expensiveObjectLazyAccess;
};
主要思想是将返回昂贵对象的函数作为成员变量而不是对象本身。在构造函数中使用以下函数进行初始化:
- 初始化昂贵对象
- 用一个函数替换自己,该函数捕获已经初始化的对象并返回它。
- 返回对象。
我喜欢这种方法的原因是即使只有在第一次查询昂贵对象时才会执行初始化代码,但初始化代码仍然写在构造函数中(如果不需要懒加载,我自然会把它放在那里)。
这种方法的缺点是std::function在执行过程中会重新分配自己。在重新分配后访问任何非静态成员(在使用lambda时捕获)将导致未定义的行为,因此需要额外注意。此外,这种方法有点像黑客技术,因为GetExpensiveObject()是const,但它仍然在第一次调用时修改了成员属性。
在生产代码中,我可能更喜欢像James Curran所描述的那样将成员变量设置为mutable。这样,您类的公共API清楚地说明该成员不被视为对象状态的一部分,因此不会影响constness。
经过更深入的思考,我发现可以使用带有std::launch::deferred参数的std::async与std::shared_future相结合,以便能够多次检索结果。以下是代码:
class MyClass
{
public:
MyClass() :
deferredObj()
{
deferredObj = std::async(std::launch::deferred, []()
{
return std::shared_ptr<ExpensiveType>(CreateExpensiveObject());
});
}
const ExpensiveType* GetExpensiveObject() const
{
return deferredObj.get().get();
}
private:
std::shared_future<std::shared_ptr<ExpensiveType>> deferredObj;
};