誰かが(できれば平易な英語を使って)どのように機能するか説明してもらえますかstd::flush
?
- それは何ですか?
- いつストリームをフラッシュしますか?
- どうしてそれが重要ですか?
ありがとうございました。
何 が起こったのかは答えられなかったstd::flush
ので、ここにそれが実際に何であるかについての詳細があります。マニピュレータstd::flush
、つまり特定のシグネチャを持つ関数です。簡単に始めるために、あなたは署名を持っていると考えることができますstd::flush
std::ostream& std::flush(std::ostream&);
ただし、現実はもう少し複雑です(興味がある場合は、以下でも説明します)。
この形式の演算子をとるストリームクラスのオーバーロード出力演算子。つまり、引数としてマニピュレータをとるメンバー関数があります。出力演算子は、オブジェクト自体を使用してマニピュレータを呼び出します。
std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
(*manip)(*this);
return *this;
}
つまり、で「出力」するとstd::flush
、std::ostream
対応する関数が呼び出されるだけです。つまり、次の2つのステートメントは同等です。
std::cout << std::flush;
std::flush(std::cout);
さて、std::flush()
それ自体はかなり単純です。それが行うのは、を呼び出すstd::ostream::flush()
ことだけです。つまり、その実装を次のように想像することができます。
std::ostream& std::flush(std::ostream& out) {
out.flush();
return out;
}
このstd::ostream::flush()
関数はstd::streambuf::pubsync()
、ストリームに関連付けられているストリームバッファ(存在する場合)を技術的に呼び出します。ストリームバッファは、使用されているバッファがオーバーフローした場合、または内部表現を外部宛先、つまりデータをフラッシュする場合。外部宛先と同期するシーケンシャルストリームでは、バッファリングされた文字がすぐに送信されることを意味します。つまり、を使用するstd::flush
と、ストリームバッファはその出力バッファをフラッシュします。たとえば、データがコンソールに書き込まれると、フラッシュにより、コンソールのこの時点で文字が表示されます。
これは疑問を投げかけるかもしれません:なぜ文字はすぐに書かれないのですか?簡単な答えは、文字を書くのは一般的にかなり遅いということです。ただし、妥当な量の文字を書き込むのにかかる時間は、基本的に1つだけを書き込むのと同じです。文字数は、オペレーティングシステムやファイルシステムなどの多くの特性によって異なりますが、多くの場合、最大4k文字が1文字とほぼ同時に書き込まれます。したがって、外部宛先の詳細に応じて、バッファを使用して送信する前に文字をバッファリングすることで、パフォーマンスを大幅に向上させることができます。
上記はあなたの3つの質問のうちの2つに答えるはずです。残りの質問は次のとおりです。ストリームをいつフラッシュしますか?答えは次のとおりです。文字を外部の宛先に書き込む必要がある場合!これは、ファイルの書き込みの最後(ただし、ファイルを閉じると暗黙的にバッファがフラッシュされます)、またはユーザー入力を要求する直前(そのままの状態から読み取るときに自動的にフラッシュされることに注意してください)のstd::cout
場合があります。ストリームを明示的にフラッシュしたい場合がいくつかあるかもしれませんが、私はそれらがかなりまれであると思います。std::cin
std::cout
std::istream::tie()
std::cin
最後に、実際の内容の全体像を示すことを約束しましたstd::flush
。ストリームは、さまざまな文字タイプを処理できるクラステンプレートです(実際には、ストリームは別の文字で動作char
しwchar_t
ます。実際に決心していれば実行可能ですが、別の文字で動作させることは非常に複雑です。 )。ストリームのすべてのインスタンス化で使用できるようにするためにstd::flush
、これはたまたま次のようなシグネチャを持つ関数テンプレートです。
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
std::flush
インスタンス化してすぐに使用する場合std::basic_ostream
は、実際には重要ではありません。コンパイラは、テンプレート引数を自動的に推測します。ただし、この関数がテンプレート引数の推定を容易にするものと一緒に言及されていない場合、コンパイラはテンプレート引数の推定に失敗します。
デフォルトでは、std::cout
はバッファリングされ、実際の出力は、バッファがいっぱいになるか、他のフラッシュ状況(たとえば、ストリーム内の改行)が発生した場合にのみ出力されます。印刷がすぐに行われることを確認したい場合があり、手動でフラッシュする必要があります。
たとえば、1つのドットを印刷して進捗レポートを報告するとします。
for (;;)
{
perform_expensive_operation();
std::cout << '.';
std::flush(std::cout);
}
フラッシュがないと、出力は非常に長い間表示されません。
std::endl
ストリームに改行を挿入するだけでなく、フラッシュさせることに注意してください。フラッシングはやや高価なのでstd::endl
、フラッシングが明確に望まれない場合は過度に使用しないでください。
これは、フラッシュが何をしているのかを観察するために書くことができる短いプログラムです。
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
cout << "Line 1..." << flush;
usleep(500000);
cout << "\nLine 2" << endl;
cout << "Line 3" << endl ;
return 0;
}
このプログラムを実行します。1行目が印刷され、一時停止してから2行目と3行目が印刷されます。次に、フラッシュ呼び出しを削除してプログラムを再度実行します。プログラムが一時停止してから、3行すべてが印刷されます。同時。最初の行はプログラムが一時停止する前にバッファリングされますが、バッファがフラッシュされることはないため、行1は行2からのendl呼び出しまで出力されません。
ストリームは何かに接続されています。標準出力の場合、コンソール/画面であるか、パイプまたはファイルにリダイレクトされる可能性があります。プログラムと、たとえばファイルが保存されているハードディスクとの間には多くのコードがあります。たとえば、オペレーティングシステムが任意のファイルで処理を行っている場合や、ディスクドライブ自体がデータをバッファリングして、固定サイズのブロックに書き込むことができるようにしたり、より効率的にしたりする場合があります。
ストリームをフラッシュすると、言語ライブラリ、OS、およびハードウェアに、これまでに出力した文字をストレージに強制的に送信するように指示します。理論的には、「フラッシュ」の後、コードを壁から蹴り出すことができ、それらのキャラクターは安全に保管されます。
OSドライバーを書いている人やディスクドライブを設計している人は、提案として「フラッシュ」を自由に使用でき、実際には文字を書き出せない可能性があることに注意してください。出力が閉じている場合でも、保存するまでしばらく待つ場合があります。(OSはあらゆる種類の処理を一度に実行するため、バイトを処理するために1〜2秒待つ方が効率的である可能性があることに注意してください。)
したがって、フラッシュは一種のチェックポイントです。
もう1つの例:出力がコンソール表示に送られる場合、フラッシュにより、文字が実際にユーザーが見ることができる場所まで完全に到達することが確認されます。これは、キーボード入力を期待しているときに行うべき重要なことです。コンソールに質問を書き込んでも、それがまだどこかの内部バッファーに残っていると思われる場合、ユーザーは回答を入力する内容がわかりません。したがって、これはフラッシュが重要な場合です。