std :: flush는 어떻게 작동합니까?


84

누군가가 어떻게 std::flush작동 하는지 설명해 주시겠습니까 (가급적 일반 영어 사용) ?

  • 뭐야?
  • 언제 하천을 비우시겠습니까?
  • 왜 중요 함?

감사합니다.

답변:


137

무슨 std::flush 일이 벌어 질지대한 답변이 없었기 때문에 여기에 실제로 무엇이 있는지에 대한 세부 사항이 있습니다. std::flushA는 조작 , 즉, 특정의 서명 기능. 간단하게 시작하려면 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, 그냥 해당 함수를 호출, 즉, 다음 두 명령문은 동일합니다 :

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하면 스트림 버퍼가 출력 버퍼를 플러시합니다. 예를 들어, 데이터가 콘솔에 기록 될 때 플러싱으로 인해 콘솔의이 지점에 문자가 나타납니다.

이것은 질문을 제기 할 수 있습니다. 문자가 즉시 작성되지 않는 이유는 무엇입니까? 간단한 대답은 문자 쓰기가 일반적으로 상당히 느리다는 것입니다. 그러나 합리적인 양의 문자를 쓰는 데 걸리는 시간은 본질적으로 어디에 하나만 쓰는 것과 동일합니다. 문자의 양은 운영 체제, 파일 시스템 등의 여러 특성에 따라 다르지만 종종 최대 4k 문자가 한 문자와 거의 같은 시간에 기록됩니다. 따라서 외부 대상의 세부 사항에 따라 버퍼를 사용하여 문자를 보내기 전에 버퍼링하면 성능이 크게 향상 될 수 있습니다.

위의 질문은 세 가지 질문 중 두 가지에 답해야합니다. 나머지 질문은 : 언제 스트림을 플러시 할 것입니까? 대답은 다음과 같습니다. 문자를 외부 대상에 기록해야 할 때! 이 파일을 작성하는 단부에있을 수있다 (참고 사용자 입력을 요청하기 전에 즉시 (암시 파일을 닫는 비록 버퍼를 플러시) std::cout로부터 판독 될 때 자동적으로 플러싱 std::cinstd::coutstd::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그것은 정말 문제가되지 않습니다 : 컴파일러의 추론 템플릿 인수를 자동으로. 그러나이 함수가 템플릿 인수 추론을 용이하게하는 것과 함께 언급되지 않은 경우 컴파일러는 템플릿 인수 추론에 실패합니다.


3
환상적인 대답입니다. 다른 곳에서는 플러시가 어떻게 작동하는지에 대한 품질 정보를 찾을 수 없습니다.
마이클

31

기본적으로 std::cout는 버퍼링되며 실제 출력은 버퍼가 가득 차거나 다른 플러싱 상황이 발생했을 때만 인쇄됩니다 (예 : 스트림의 줄 바꿈). 경우에 따라 인쇄가 즉시 이루어 지도록하고 수동으로 플러시해야합니다.

예를 들어, 단일 점을 인쇄하여 진행률 보고서를보고한다고 가정합니다.

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

플러싱이 없으면 오랫동안 출력을 볼 수 없습니다.

참고 std::endl플러시에 원인뿐만 아니라 스트림에 줄 바꿈을 삽입합니다. 플러싱은 약간 비싸기 때문에 플러싱이 std::endl명시 적으로 원하지 않는 경우 과도하게 사용해서는 안됩니다.


7
독자를위한 추가 참고 사항 : coutC ++에서 버퍼링되는 유일한 것은 아닙니다. ostream일반적으로 s는 기본적으로 버퍼링되며 fstreams 등도 포함 합니다.
Cornstalks dec

또한 using cin은 플러시되기 전에 출력 을 수행합니다.
Zaid Khan

21

다음은 플러시가하는 일을 관찰하기 위해 작성할 수있는 짧은 프로그램입니다.

#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 행을 인쇄합니다. 이제 flush 호출을 제거하고 프로그램을 다시 실행하십시오. 프로그램이 일시 정지 된 다음 3 행 모두를 같은 시간. 첫 번째 라인은 프로그램이 일시 중지되기 전에 버퍼링되지만 버퍼가 플러시되지 않기 때문에 라인 1은 라인 2에서 endl 호출이있을 때까지 출력되지 않습니다.


이를 설명하는 또 다른 방법은 cout << "foo" << flush; std::abort();. 주석 처리 / 제거 << flush하면 출력이 없습니다! 추신 : 호출하는 표준 아웃 디버깅 DLL abort은 악몽입니다. DLL은 abort.
Mark Storer

5

스트림은 무언가에 연결되어 있습니다. 표준 출력의 경우 콘솔 / 화면이거나 파이프 또는 파일로 리디렉션 될 수 있습니다. 프로그램과 예를 들어 파일이 저장된 하드 디스크 사이에는 많은 코드가 있습니다. 예를 들어, 운영 체제는 모든 파일에 대해 작업을 수행하거나 디스크 드라이브 자체가 데이터를 버퍼링하여 고정 크기 블록에 기록하거나 더 효율적일 수 있습니다.

스트림을 플러시하면 지금까지 출력 한 모든 문자를 저장하도록 강제 할 언어 라이브러리, OS 및 하드웨어에 알립니다. 이론적으로 '플러시'후 벽에서 코드를 걷어 내면 해당 문자는 여전히 안전하게 보관됩니다.

나는 os 드라이버를 작성하는 사람들이나 디스크 드라이브를 설계하는 사람들이 제안으로 'flush'를 자유롭게 사용할 수 있으며 실제로 문자를 쓰지 않을 수도 있음을 언급해야합니다. 출력이 닫혀 있어도 저장을 위해 잠시 기다릴 수 있습니다. (OS는 모든 종류의 일을 한 번에 수행하며 바이트를 처리하기 위해 1 ~ 2 초를 기다리는 것이 더 효율적일 수 있습니다.)

따라서 플러시는 일종의 체크 포인트입니다.

한 가지 더 예 : 출력이 콘솔 디스플레이로 이동하는 경우 플러시는 문자가 실제로 사용자가 볼 수있는 곳으로 완전히 빠져 나가도록합니다. 이것은 키보드 입력을 기대할 때해야 할 중요한 일입니다. 콘솔에 질문을 작성했는데 여전히 어딘가에 내부 버퍼에 갇혀 있다고 생각한다면 사용자는 답을 입력해야할지 모릅니다. 그래서 이것은 플러시가 중요한 경우입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.