如何实现RAII +延迟初始化?

时间:2016-10-02 03:18:18

标签: c++ lazy-initialization raii

是否有可能在C ++中实现两者兼而有之的设计 - RAII,以确保资源安全释放,和 - 懒惰初始化,只有在真正使用资源时才获取资源。

我的想法是,只需实现一个懒惰的初始化,而在实际资源获取中,使用RAII。

行业惯例如何?

2 个答案:

答案 0 :(得分:3)

是的,这是可能的。只需使用std::optional(C ++ 17或来自Boost)或unique_ptr / shared_ptr

(意见)optional在可读性方面有很大的优势 - 你不能更清楚地知道这个值可能没有被初始化。

要显示资源是否正确释放:首先让我们从急切的初始化开始(live):

ofstream file("test.txt");
file << "no flush\n";

ifstream inFile("test.txt");
string line;
getline(inFile, line);
cout << line << endl;

这个,不打印任何东西¹。让我们将写作移到单独的范围(live):

{
    ofstream file("test.txt");
    file << "no flush\n";
}

ifstream inFile("test.txt");
string line;
getline(inFile, line);
cout << line << endl;

这应打印no flush,因为ofstream在销毁时保证close()文件。 (除非同时访问其他内容test.txt

现在使用Boost.Optional和lazy init(live):

{
    boost::optional<std::ofstream> file;

    file = ofstream("test.txt");
    file.get() << "no flush\n";
}

ifstream inFile("test.txt");
string line;
getline(inFile, line);
cout << line << endl;

资源与常规ofstream同时发布。

¹文件访问不保证是缓冲的,但它也是一个很好的例子,也可以在在线编译器上使用。

答案 1 :(得分:1)

通常的做法是尽可能避免延迟初始化。

如果存在延迟初始化方案,则在实际初始化之前,没有任何东西阻止对象的调用者(或用户)做依赖于初始化对象的事情。这可能导致混乱。

为了解决这个问题,对象(或类)实现需要跟踪对象是否实际初始化。这使得类实现更加复杂 - 如果任何成员函数忘记检查对象是否被初始化,或者如果任何成员函数将对象置于无效状态,则可能发生混乱。如果对象(或类)不这样做,则该类更难使用,因为使用该类的代码的任何错误都会导致问题。

相反,更常用的技术是(1)构造函数建立一个不变的(2)成员函数假设维持不变量和(3)析构函数清理。

换句话说,构造函数初始化对象,成员函数确保对象保持合理状态。允许成员函数ASSUME对象在被调用时处于有效状态....所以不需要检查。只要所有成员函数确保对象在返回时仍处于有效状态,就没有问题。

唯一的例外是析构函数,它导致对象不再存在。换句话说,在销毁一个对象(调用它的析构函数)之后,根本不应该使用该对象的任何成员。

对于调用者来说,这很简单 - 在创建对象所需的信息可用之前不要创建对象。换句话说,而不是

 SomeObject object;

 // gather data needed to initialise object

 //   Danger, danger: it is possible to mistakenly use object as if it is initialised here

 object.initialise(needed_data);

 // do other things with object

 //object ceases to exist (e.g. end of {} block).

这样做

 // gather data needed to initialise object

 //   note that the compiler will reject using object here

 SomeObject object(needed_data);

 // do other things with object

 //object ceases to exist (e.g. end of {} block).

在C ++中,没有什么能阻止在需要时创建对象。变量不限于在块的顶部或类似的地方声明。