誰かが(できれば平易な英語を使って)どのように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()
D」に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
、フラッシングが明確に望まれない場合は過度に使用しないでください。
cout
C ++でバッファリングされるのはそれだけではありません。ostream
一般に、sは通常、デフォルトでバッファリングされます。これには、fstream
sなども含まれます。
cin
すると、フラッシュされる前に出力が実行されます。
これは、フラッシュが何をしているのかを観察するために書くことができる短いプログラムです。
#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呼び出しまで出力されません。
cout << "foo" << flush; std::abort();
です。コメントアウト/削除した場合<< flush
、出力はありません!PS:呼び出す標準出力デバッグDLLabort
は悪夢です。DLLは決してを呼び出すべきではありませんabort
。
ストリームは何かに接続されています。標準出力の場合、コンソール/画面であるか、パイプまたはファイルにリダイレクトされる可能性があります。プログラムと、たとえばファイルが保存されているハードディスクとの間には多くのコードがあります。たとえば、オペレーティングシステムが任意のファイルを処理している場合や、ディスクドライブ自体がデータをバッファリングして、固定サイズのブロックに書き込むことができるようにしたり、より効率的にしたりする場合があります。
ストリームをフラッシュすると、言語ライブラリ、OS、およびハードウェアに、これまでに出力したすべての文字を強制的にストレージに保存するように指示します。理論的には、「フラッシュ」の後、コードを壁から蹴り出すことができ、それらの文字は引き続き安全に保管されます。
OSドライバーを書いている人やディスクドライブを設計している人は、提案として「フラッシュ」を自由に使用でき、実際には文字を書き出せない可能性があることに注意してください。出力が閉じている場合でも、保存するまでしばらく待つ場合があります。(OSはすべての種類のことを一度に実行するので、バイトを処理するために1〜2秒待つ方が効率的である可能性があることに注意してください。)
したがって、フラッシュは一種のチェックポイントです。
もう1つの例:出力がコンソールディスプレイに送信される場合、フラッシュにより、ユーザーが表示できる場所まで文字が実際に表示されるようになります。これは、キーボード入力を期待しているときに行う重要なことです。コンソールに質問を書き込んでも、それがまだどこかの内部バッファーに残っていると思われる場合、ユーザーは回答を入力する方法がわかりません。したがって、これはフラッシュが重要な場合です。