누군가가 어떻게 std::flush
작동 하는지 설명해 주시겠습니까 (가급적 일반 영어 사용) ?
- 뭐야?
- 언제 하천을 비우시겠습니까?
- 왜 중요 함?
감사합니다.
답변:
무슨 std::flush
일이 벌어 질지 에 대한 답변이 없었기 때문에 여기에 실제로 무엇이 있는지에 대한 세부 사항이 있습니다. std::flush
A는 조작 , 즉, 특정의 서명 기능. 간단하게 시작하려면 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::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
는 버퍼링되며 실제 출력은 버퍼가 가득 차거나 다른 플러싱 상황이 발생했을 때만 인쇄됩니다 (예 : 스트림의 줄 바꿈). 경우에 따라 인쇄가 즉시 이루어 지도록하고 수동으로 플러시해야합니다.
예를 들어, 단일 점을 인쇄하여 진행률 보고서를보고한다고 가정합니다.
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 행을 인쇄합니다. 이제 flush 호출을 제거하고 프로그램을 다시 실행하십시오. 프로그램이 일시 정지 된 다음 3 행 모두를 같은 시간. 첫 번째 라인은 프로그램이 일시 중지되기 전에 버퍼링되지만 버퍼가 플러시되지 않기 때문에 라인 1은 라인 2에서 endl 호출이있을 때까지 출력되지 않습니다.
cout << "foo" << flush; std::abort();
. 주석 처리 / 제거 << flush
하면 출력이 없습니다! 추신 : 호출하는 표준 아웃 디버깅 DLL abort
은 악몽입니다. DLL은 abort
.
스트림은 무언가에 연결되어 있습니다. 표준 출력의 경우 콘솔 / 화면이거나 파이프 또는 파일로 리디렉션 될 수 있습니다. 프로그램과 예를 들어 파일이 저장된 하드 디스크 사이에는 많은 코드가 있습니다. 예를 들어, 운영 체제는 모든 파일에 대해 작업을 수행하거나 디스크 드라이브 자체가 데이터를 버퍼링하여 고정 크기 블록에 기록하거나 더 효율적일 수 있습니다.
스트림을 플러시하면 지금까지 출력 한 모든 문자를 저장하도록 강제 할 언어 라이브러리, OS 및 하드웨어에 알립니다. 이론적으로 '플러시'후 벽에서 코드를 걷어 내면 해당 문자는 여전히 안전하게 보관됩니다.
나는 os 드라이버를 작성하는 사람들이나 디스크 드라이브를 설계하는 사람들이 제안으로 'flush'를 자유롭게 사용할 수 있으며 실제로 문자를 쓰지 않을 수도 있음을 언급해야합니다. 출력이 닫혀 있어도 저장을 위해 잠시 기다릴 수 있습니다. (OS는 모든 종류의 일을 한 번에 수행하며 바이트를 처리하기 위해 1 ~ 2 초를 기다리는 것이 더 효율적일 수 있습니다.)
따라서 플러시는 일종의 체크 포인트입니다.
한 가지 더 예 : 출력이 콘솔 디스플레이로 이동하는 경우 플러시는 문자가 실제로 사용자가 볼 수있는 곳으로 완전히 빠져 나가도록합니다. 이것은 키보드 입력을 기대할 때해야 할 중요한 일입니다. 콘솔에 질문을 작성했는데 여전히 어딘가에 내부 버퍼에 갇혀 있다고 생각한다면 사용자는 답을 입력해야할지 모릅니다. 그래서 이것은 플러시가 중요한 경우입니다.