web-dev-qa-db-ja.com

std :: flushはどのように機能しますか?

誰かが(できれば平易な英語を使用して)説明してくださいstd::flush動作しますか?

  • それは何ですか?
  • いつストリームをフラッシュしますか?
  • どうしてそれが重要ですか?

ありがとうございました。

66
Dasaru

whatstd::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::flushstd::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::coutstd::cinstd::istream::tie() 'であるときにstd::coutが自動的にフラッシュされることに注意してください) 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を使用する場合、実際には問題ではありません。コンパイラーはテンプレート引数を自動的に推測します。ただし、この関数がテンプレート引数の推論を容易にするものと一緒に言及されていない場合、コンパイラはテンプレート引数の推論に失敗します。

107
Dietmar Kühl

デフォルトでは、std::coutはバッファリングされ、実際の出力は、バッファがいっぱいになるか、その他のフラッシュ状況(ストリーム内の改行など)が発生した場合にのみ出力されます。印刷がすぐに行われるようにしたい場合があり、手動でフラッシュする必要があります。

たとえば、単一のドットを印刷して進行状況レポートを報告するとします。

for (;;)
{
    perform_expensive_operation();
    std::cout << '.';
    std::flush(std::cout);
}

フラッシュしないと、出力が非常に長い間表示されません。

ご了承ください std::endlは、ストリームにフラッシュを挿入するだけでなく、ストリームに改行を挿入します。フラッシュはやや高価なので、std::endlは、フラッシュが明示的に望まれない場合、過度に使用すべきではありません。

25
Kerrek SB

フラッシュが何をしているかを観察するために書くことができる短いプログラムはここにあります

#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行目を印刷します。同時。最初の行はプログラムが一時停止する前にバッファリングされますが、バッファがフラッシュされることはないため、2行目からendlが呼び出されるまで1行目は出力されません。

11
Neil Anderson

ストリームは何かに接続されています。標準出力の場合、コンソール/画面であるか、パイプまたはファイルにリダイレクトされる可能性があります。プログラムと、たとえばファイルが保存されているハードディスクとの間には多くのコードがあります。たとえば、オペレーティングシステムが任意のファイルで処理を行っているか、ディスクドライブ自体がデータをバッファリングして固定サイズのブロックに書き込むか、単に効率を上げている可能性があります。

ストリームをフラッシュすると、これまでに出力した文字をストレージに強制的に移動するように、言語ライブラリ、OS、ハードウェアに指示します。理論的には、「フラッシュ」の後、壁からコードを蹴り出すことができ、それらのキャラクターはまだ安全に保管されます。

OSドライバーを書いている人やディスクドライブを設計している人は、提案として「フラッシュ」を自由に使用でき、実際に文字を書き出せない可能性があることに注意してください。出力が閉じられている場合でも、保存するまでしばらく待つ場合があります。 (OSはすべての種類の処理を一度に行うことを覚えておいてください。バイトを処理するのに1、2秒待つ方が効率的かもしれません。)

したがって、フラッシュは一種のチェックポイントです。

もう1つの例:出力がコンソールディスプレイに送られる場合、フラッシュにより、ユーザーが実際に見ることができる場所までキャラクターが実際に完全に抜けることが確認されます。これは、キーボード入力を期待しているときに行うべき重要なことです。コンソールに質問を書いたが、それがまだどこかの内部バッファに残っていると思われる場合、ユーザーは答えを入力するのか分からない。したがって、これはフラッシュが重要な場合です。

4
Lee Meador