Podstawową ideą buforowania jest łączenie operacji w większe części: zamiast czytać małą liczbę bajtów, przeczytaj całą stronę i udostępnij ją zgodnie z wymaganiami; zamiast zapisywać małą liczbę bajtów, buforuj je i pisz całą stronę lub gdy wyraźnie zażądasz zapisu. Zasadniczo jest to ważna optymalizacja wydajności. Jest całkiem czytelny dla operacji we/wy, ale generalnie odnosi się również do innych zastosowań: przetwarzanie wielu jednostek na raz generalnie kończy się szybciej niż przetwarzanie pojedynczych jednostek.
W odniesieniu do I/O spłukiwanie odnosi się do zapisywania aktualnie buforowanych bajtów do miejsca docelowego - cokolwiek to oznacza w praktyce. Dla C++ IOStream płukanie strumienia oznacza wywołanie funkcji składowej std::ostream::flush()
, która z kolei wywołuje std::streambuf::pubsync()
na skojarzonym buforze strumienia (to ignoruje szczegóły, że strumień jest w rzeczywistości wzorcami klas, np.std::basic_ostream<cT, traits>
; dla celów niniejszej dyskusji nie ma znaczenia, że są szablonami klas): podstawowa klasa std::streambuf
jest abstrakcją C++ dotyczącą sposobu przetwarzania określonego strumienia. Koncepcyjnie składa się z bufora wejściowego i wyjściowego oraz funkcji wirtualnej odpowiedzialnej za odczyt lub zapis buforów. Funkcja std::streambuf::pubsync()
wywołuje funkcję wirtualną std::streambuf::sync()
, którą należy przesłonić dla każdego bufora strumieniowego, który potencjalnie buforuje znaki. Oznacza to, że to, co w rzeczywistości oznacza, to zależy od sposobu implementacji tej funkcji wirtualnej.
czy override sync()
rzeczywiście coś robi i co robi wyraźnie zależy od tego bufor strumienia reprezentuje. Na przykład dla std::filebuf
, który jest odpowiedzialny za odczyt lub zapis do pliku, sync()
zapisuje bieżącą zawartość bufora i usuwa wypróżnione znaki z bufora. Biorąc pod uwagę, że plik może nie odzwierciedlać fizycznego pliku, ale np. Nazwany potok do komunikacji z innym procesem, to rozsądne zachowanie. Z drugiej strony, przepłukanie std::stringbuf
, które jest buforem strumieniowym stosowanym do realizacji zapisu do użytego std::string
, np. przez std::ostringstream
w rzeczywistości nic nie robi: ciąg jest w całości w programie, a std::string
reprezentujący jego wartość jest konstruowany po wywołaniu funkcji składowej std::stringbuf::str()
. Dla zdefiniowanych przez użytkownika buforów strumieni istnieje wiele różnych zachowań. Typową klasą buforów strumieniowych jest filtrowanie wyjścia w pewien sposób przed przekazaniem go do innego bufora strumieniowego (np. Bufor rejestrujący może dodać znacznik czasu po każdym nowym wierszu). Zwykle nazywają one funkcję std::streambuf::pubsync()
następnych buforów strumieni.
W związku z tym opisy faktycznego zachowania są zwykle utrzymywane dość niejasne, ponieważ nie jest jasne, co dokładnie się dzieje. Koncepcyjnie, przepłukanie strumienia lub wywołanie pubsync()
w buforze strumieniowym powinno zaktualizować zewnętrzny cel znaku, aby dopasować go do bieżącego stanu wewnętrznego. Ogólnie rzecz biorąc, oznacza to przekazywanie aktualnie buforowanych znaków i usuwanie ich z wewnętrznego bufora. W tym miejscu warto zauważyć, że bufor jest zwykle zapisywany, gdy jest po prostu pełny. Kiedy stanie się pełny, zależy ponownie od konkretnego bufora strumieniowego. Dobra implementacja std::filebuf
zasadniczo wypełni bufor bajtów odpowiadający rozmiarowi bazowej strony (lub jego wielokrotności), a następnie zapisze pełne strony, minimalizując liczbę operacji we/wy potrzebnych (jest to stosunkowo trudne, ponieważ bufor rozmiary różnią się między różnymi systemami plików iw zależności od kodowania używanego przy pisaniu liczby produkowanych bajtów nie da się łatwo oszacować).
Standardowa biblioteka C++ zwykle nie wymaga wyraźnych flushes:
- strumienia
std::cerr
jest skonfigurowany do automatycznego spłukiwania żadnego wyjścia produkowanego po każdej operacji wyjściowe miano. Jest to wynik ustawienia domyślnego flagi formatowania std::ios_base::unitbuf
. Aby wyłączyć tę funkcję, możesz użyć std::cerr << std::nounitbuf
lub możesz po prostu użyć std::clog
, która zapisuje do tego samego miejsca docelowego, ale nie robi tego płukania.
- Czytając od
std::istream
„związanym” std::ostream
, jeśli występuje, jest opróżniany. Domyślnie std::cout
jest związany z std::cin
. Jeśli chcesz samemu ustawić powiązanego std::ostream
możesz użyć np. in.tie(&out)
lub, jeśli chcesz usunąć związany numer std::ostream
, możesz użyć np. std::cin.tie(0)
.
- Po zniszczeniu
std::ostream
(lub w przypadku normalnego zniszczenia w przypadku, gdy std::ostream
jest jednym ze standardowych strumieni), std::ostream
jest przepłukiwany.
- Gdy bufor strumienia zostanie przepełniony, wywoływana jest funkcja wirtualna
std::streambuf::overflow()
, która zwykle zapisuje bufor do bieżącego bufora (plus przekazany znak, jeśli taki istnieje). Często odbywa się to po prostu wywołując sync()
, aby wyczyścić bieżący bufor, ale to, co się robi dokładnie, znowu, zależy od konkretnego bufora strumienia.
Można również wyraźnie zażądać spłukiwania std::ostream
:
- można wywołać funkcji członka
std::ostream::flush()
, np std::cout.flush()
.
- Możesz wywołać funkcję składową
std::streambuf::pubsync()
, np. std::cout.rdbuf()->pubsync()
(zakładając, że jest ustawiony bufor strumienia, wywołanie std::ostream::flush()
nic nie da, jeśli nie ma bufora strumieniowego).
- Można użyć manipulatora
std::flush
, np. std::cout << std::flush
.
- Można użyć manipulatora
std::endl
, np. std::cout << std::endl
, aby najpierw wpisać znak nowej linii, a następnie przepłukać strumień. Zauważ, że powinieneś używać std::endl
tylko, kiedy naprawdę chcesz opróżnić wyjście. Nie używaj używaj std::endl
, gdy chcesz po prostu utworzyć koniec linii! Po prostu napisz znak nowej linii dla tego ostatniego!
W każdym razie, jeśli napiszesz tylko kilka znaków, które nie powodują przepełnienia bufora bufora strumieniowego, nic się z nimi nie stanie, dopóki nie zostaną one pośrednio lub jawnie przepłukane. Zasadniczo nie stanowi to problemu, ponieważ normalny przypadek, w którym buforowane wyjście musi stać się dostępne, to jest podczas odczytu z std::cin
, jest obsługiwany przez std::cout
będący tie()
d do std::cin
. Gdy są używane inne mechanizmy wejścia/wyjścia, może być konieczne jawne strumieniowanie powiązane. Bufor jest czasem problemem, jeśli program "zawiesza się" lub zapewnia podczas debugowania pewne wyjścia do strumienia, które mogły zostać utworzone, ale jeszcze nie zostały wysłane. Rozwiązaniem tego problemu jest użycie std::unitbuf
.
to pytanie zostało zadane i udzielono odpowiedzi na wiele pytań w SO. http://stackoverflow.com/a/4752069/1155650 –