リソースは安全に解放されることを保証するために、 - RAIIとリソースが実際に使用された場合にのみ取得されるようなレイジー初期化の両方のデザインをC++で実装することは可能ですか?RAII +遅延初期化を実装する方法は?
私の考えは、レイジー初期設定として実装していますが、実際のリソース取得ではRAIIを使用しています。
業界の実践はどうですか?
リソースは安全に解放されることを保証するために、 - RAIIとリソースが実際に使用された場合にのみ取得されるようなレイジー初期化の両方のデザインをC++で実装することは可能ですか?RAII +遅延初期化を実装する方法は?
私の考えは、レイジー初期設定として実装していますが、実際のリソース取得ではRAIIを使用しています。
業界の実践はどうですか?
はい、可能です。 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;
これ、me¹のために何を印刷しません。さんは別々のスコープ(live)への書き込みを移動してみましょう:ofstream
がclose()
に破壊時にファイルが保証されているため、
{
ofstream file("test.txt");
file << "no flush\n";
}
ifstream inFile("test.txt");
string line;
getline(inFile, line);
cout << line << endl;
はこれ、
no flush
を印刷する必要があります。 Boost.Optionalと怠惰のinit(
live)と
そして今を(何か他のものが同時にtest.txt
にアクセスしていない限り):
{
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)コンストラクタがinvariant(2)メンバ関数を設定し、(3)destructorsがその不変を維持すると仮定します。
つまり、コンストラクタはオブジェクトを初期化し、メンバ関数はオブジェクトを適切な状態に保ちます。メンバ関数は、オブジェクトが呼び出されたときに有効な状態にあると仮定することができます。したがって、チェックする必要はありません。すべてのメンバ関数が、オブジェクトが返っても有効な状態にあることを保証している限り、問題はありません。
唯一の例外は、オブジェクトの存在を停止させるデストラクタです。つまり、オブジェクトを破棄した後(そのデストラクタを呼び出す)、そのオブジェクトのメンバはまったく使用されません。
発信者にとっては、簡単です。作成に必要な情報が利用できるようになるまで、オブジェクトを作成しないでください。代わりに
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).
は、C++では、この
// 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).
を行う他の言葉では、それが必要とされるときに作成されるオブジェクトを防ぐものは何もありません。変数は、ブロックの先頭またはそのようなもので宣言されることに限定されません。
したがって、初期化するコンストラクタは、メンバー関数がリソースをvalide状態にあると仮定してデストラクタがクリーンアップするまで使用できるように初期化するため、遅延初期化をお勧めしません。しかし、どこにでも怠惰な初期化があります。私は混乱している、これはC + +の特別な練習することはできません。人々がまだSingleton(http://www.onelib.org/article/singletons-solving-problems-you-didnt-know-you-never-had-since-1995)を使用しているように、暴徒が間違っていることを意味しますか? – athos
もちろん、遅延初期化が使用される場合があります。これを行うことが理にかなっている状況がいくつかありますが、それらの多くは単にCからの二日酔いです。実際には、オブジェクトを作成し、それを初期化することが別々の操作であることがしばしば必要です。その分離は、コンストラクタでオブジェクトを作成する行為は、(コンパイラによって管理されるようにする詳細はありますが)一段階で初期化するため、C++ではほとんど必要ありません。 RAIIの初期化とレイジー初期化とを混在させることについて尋ねました。これは、怠惰な初期化が悪い考えである1つのケースです。 – Peter
説明をありがとう – athos
はい、もちろん可能です。業界の慣習として、私はそのようなオブジェクトの既存のアプリケーションを認識していません。 –
@ MarkRansomなぜそのような実装はありませんか?そのようなコンセプトは合理的ではないのでしょうか? – athos
概念を一緒に実装する必要はありません。あなたの遅延オブジェクトに 'shared_ptr'または' unique_ptr'を使用するだけです。 –