2012-12-31 20 views
46

Czy ktoś może wyjaśnić (najlepiej używając zwykłego angielskiego), jak działa std::flush?Jak działa std :: flush?

  • Co to jest?
  • Kiedy wypłukasz strumień?
  • Dlaczego to jest ważne?

Dziękuję.

Odpowiedz

74

Ponieważ nie została odebrana co to jeststd::flush, oto kilka szczegółów na temat tego, co to jest. std::flush jest manipulatorem , tj. Funkcją o określonej sygnaturze.Aby rozpocząć proste, można myśleć std::flush posiadania podpisu

std::ostream& std::flush(std::ostream&); 

Rzeczywistość jest nieco bardziej skomplikowana, chociaż (jeśli jesteś zainteresowany, to jest wyjaśnione poniżej, jak również).

Operatory wyjścia przeciążenia klasy strumienia za pomocą operatorów tego formularza, tj. Istnieje funkcja elementu przyjmująca manipulator jako argument. Operator wyjście wywołuje Manipulator z samego obiektu:

std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) { 
    (*manip)(*this); 
    return *this; 
} 

Oznacza to, że jeśli „Wyjście” std::flush z do std::ostream, to po prostu wywołuje odpowiednią funkcję, czyli dwa następujące stwierdzenia są równoważne:

std::cout << std::flush; 
std::flush(std::cout); 

teraz sama std::flush() jest dość prosta: Wszystko robi to nazwać std::ostream::flush(), czyli można sobie wyobrazić jego realizację wyglądać mniej więcej tak:

std::ostream& std::flush(std::ostream& out) { 
    out.flush(); 
    return out; 
} 

Funkcja technicznie wywołuje std::streambuf::pubsync() w buforze strumieniowym (jeśli występuje), który jest powiązany ze strumieniem: Bufor strumienia jest odpowiedzialny za buforowanie znaków i wysyłanie znaków do zewnętrznego miejsca przeznaczenia, gdy używany bufor ulegnie przepełnieniu lub gdy wewnętrzna reprezentacja powinien być zsynchronizowany z zewnętrznym miejscem docelowym, tj. kiedy dane mają zostać przepłukane. W przypadku synchronizacji sekwencyjnej z zewnętrznym miejscem docelowym oznacza to, że wszystkie buforowane znaki są natychmiast wysyłane. Oznacza to, że użycie std::flush powoduje, że bufor strumienia wypłukuje swój bufor wyjściowy. Na przykład, gdy dane są zapisywane w konsoli, spłukiwanie powoduje pojawianie się znaków w tym miejscu na konsoli.

To może nasunąć pytanie: dlaczego postacie nie są natychmiast pisane? Prosta odpowiedź jest taka, że ​​pisanie postaci jest zazwyczaj dość powolne. Jednak ilość czasu potrzebna na napisanie rozsądnej liczby znaków jest zasadniczo taka sama jak zapisanie tylko jednego miejsca. Ilość znaków zależy od wielu cech systemu operacyjnego, systemów plików itd., Ale często aż do czegoś takiego jak 4k znaki są pisane mniej więcej w tym samym czasie, co tylko jedna postać. Dlatego też buforowanie znaków przed wysłaniem ich za pomocą bufora w zależności od szczegółów zewnętrznego miejsca docelowego może być ogromną poprawą wydajności.

Powyższe powinno odpowiedzieć na dwa z trzech pytań. Pozostaje pytanie: Kiedy wypłukasz strumień? Odpowiedź brzmi: Kiedy postacie powinny być zapisane do zewnętrznego miejsca docelowego! Może to być na końcu zapisu pliku (zamknięcie pliku niejawnie opróżnia bufor) lub bezpośrednio przed pytaniem o dane wprowadzone przez użytkownika (należy pamiętać, że std::cout jest automatycznie płukany podczas odczytu z std::cin jako std::cout pod adresem std::istream::tie() 'd do std::cin). Chociaż może być kilka okazji, w których wyraźnie chcesz przepłukać strumień, uważam, że są dość rzadkie.

Wreszcie, obiecałem dać pełny obraz tego, co std::flush rzeczywiście jest: Strumienie są szablony klasy zdolnymi do czynienia z różnymi rodzajami znaków (w praktyce pracują z char i wchar_t, dzięki czemu praca z innymi postaciami jest bardzo zaangażowany chociaż wykonalne, jeśli jesteś naprawdę zdeterminowany).Aby móc korzystać ze wszystkich std::flush dawałaby strumieni, zdarza się szablon funkcji z podpisem tak:

template <typename cT, typename Traits> 
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&); 

Podczas korzystania std::flush bezpośrednio z instancji z std::basic_ostream to naprawdę nie ma znaczenia: kompilator automatycznie dedukuje argumenty szablonu. Jednak w przypadkach, gdy funkcja ta nie jest wymieniona razem z czymś, co ułatwia odjęcie od szablonu, kompilator nie wywnioskuje argumentów szablonu.

+1

Fantastyczna odpowiedź. Nie można znaleźć wysokiej jakości informacji na temat działania koloru w innym miejscu. – Michael

22

Domyślnie std::cout jest buforowany, a rzeczywiste dane wyjściowe są drukowane tylko wtedy, gdy bufor jest pełny lub wystąpi inne spłukiwanie (np. Nowa linia w strumieniu). Czasami chcesz się upewnić, że drukowanie odbywa się od razu, i musisz spłukać ręcznie.

Na przykład, załóżmy, że chcesz zgłosić sprawozdanie z postępów poprzez wydrukowanie pojedynczej kropki:

Bez spłukiwania, nie można zobaczyć wyjście na bardzo długi czas.

Należy pamiętać, że std::endl wstawia znak nowej linii do strumienia, a także powoduje wypróżnienie. Ponieważ płukanie jest umiarkowanie kosztowne, nie należy stosować nadmiernie, jeśli płukanie nie jest wyraźnie pożądane.

+4

Tylko dodatkowa uwaga dla czytelników: 'cout' nie jest jedyną rzeczą który jest buforowany w C++. 'ostream's w ogóle są zwykle buforowane domyślnie, co obejmuje również' fstream's i tym podobne. – Cornstalks

+0

Również używając 'cin' wykonuje wyjście, zanim zostanie przepłukane, nie? – Bateman

4

Strumień jest podłączony do czegoś. W przypadku standardowego wyjścia może to być konsola/ekran lub może zostać przekierowany do potoku lub pliku. Jest wiele kodu między twoim programem i, na przykład, twardym dyskiem, gdzie plik jest przechowywany. Na przykład system operacyjny robi coś z dowolnym plikiem lub sam napęd dysku może buforować dane, aby móc je zapisać w blokach o stałym rozmiarze lub po prostu być bardziej wydajnym.

Po przepłukaniu strumienia informuje biblioteki językowe, os i sprzęt, który chcesz, aby wszelkie znaki, które dotychczas zostały wyprowadzone, zostały zmusione do przechowywania. Teoretycznie, po "flush", możesz wyrzucić kabel ze ściany i te postacie będą nadal bezpiecznie przechowywane.

Należy wspomnieć, że osoby piszące sterowniki os lub osoby projektujące napęd dyskowy mogą swobodnie używać "flush" jako sugestii i mogą nie wypisać znaków. Nawet jeśli wyjście zostanie zamknięte, mogą odczekać chwilę, aby je zapisać. (Pamiętaj, że os wykonuje wszystkie rzeczy naraz i wydajniejsze może być poczekanie sekundy lub dwóch na obsłużenie twoich bajtów.)

A więc kolor jest rodzajem punktu kontrolnego.

Jeszcze jeden przykład: jeśli wyjście przechodzi na wyświetlacz konsoli, spłukiwanie sprawi, że postacie będą w rzeczywistości aż do miejsca, w którym użytkownik może je zobaczyć. Jest to ważna rzecz, gdy oczekujesz wprowadzania danych z klawiatury. Jeśli myślisz, że napisałeś pytanie do konsoli i nadal tkwi w jakimś wewnętrznym buforze, użytkownik nie wie, co wpisać w odpowiedzi. Jest to więc przypadek, w którym kolor jest ważny.

8

Oto krótki program, który można napisać, aby obserwować, co robi równo

#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; 
} 

Uruchom ten program: można zauważyć, że drukuje linii 1, wstrzymuje, a następnie drukuje linia 2 i 3. Teraz usuń wywołanie koloru i ponowne uruchomienie programu - zauważysz, że program wstrzymuje, a następnie drukuje wszystkie 3 linie w tym samym czasie. Pierwsza linia jest buforowana przed zatrzymaniem programu, ale ponieważ bufor nigdy nie jest opróżniany, linia 1 nie jest wyprowadzana aż do zakończenia połączenia z linii 2.

Powiązane problemy