



<iostream> wspiera kontrolę typów, sprzyja zmniejszeniu liczby popełnianych błędów, oraz umożliwia tworzenie rozszerzeń. Ponadto kod, którego deklaracje znajdują się w tym nagłówku, można dziedziczyć we własnych klasach.
Być może printf() nie jest wcale taki zły, a ze scanf()'em nawet da się żyć pomimo tego, że łatwo popełnić błąd przy jego wywołaniu; jednakże obie funkcje są dość ograniczone w porównaniu do możliwości procedur wejścia/wyjścia w C++. Wejście/wyjście w C++ (przy użyciu operatorów << i >>), w porównaniu do rozwiązania z języka C (printf() i scanf()) posiada następujące zalety:
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Załóżmy przykładowo, że masz do czynienia z poniższym kodem, który pobiera liczbę całkowitą ze standardowego wejścia std::cin:
Problem z tym kodem polega na tym, że nie jest on w żaden sposób zabezpieczony przed wpisaniem nieprawidłowego znaku. W szczególności, jeśli użytkownik wpisze coś co nie wygląda na liczbę całkowitą (np. znak 'x'), strumień std::cin wejdzie w "stan błędu" (ang. "failed state") i wszystkie kolejne próby odczytania czegokolwiek z niego będą powodowały natychmiastowy powrót do kodu wywołującego operator >>, bez odbierania jakichkolwiek danych. Krótko mówiąc, program wpadnie w nieskończoną pętlę; jeśli ostatnią wpisaną liczbą byłoby 42, program będzie w kółko wyświetlał Wpisano 42.
Najprostszym sposobem umożliwiającym sprawdzanie prawidłowości wpisanych danych jest przesunięcie instrukcji odczytującej dane do warunku pętli while. Na przykład:
Dzięki temu pętla while zakończy działanie z chwilą, gdy strumień dotrze do końca pliku, gdy użytkownik wpisze coś co nie będzie liczbą całkowitą, lub gdy wpisze -1.
(Naturalnie można wyeliminować instrukcję break zmieniając treść instrukcji while z while (std::cin >> i) na while ((std::cin >> i) && (i != -1)); ale tak naprawdę nie o to chodzi w tym faqu; ma on za zadanie wyjaśnienie działania strumieni <iostreams>, a nie udzielanie ogólnych wskazówek odnośnie programowania strukturalnego)
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
W poprzednim faqu możesz zobaczyć przykład z użyciem "tej śmiesznej instrukcji while (std::cin >> foo)".
Wyrażenie (std::cin >> foo) wywołuje odpowiednią wersję operatora >> (np., funkcję operator>> która pobiera "z lewej strony" argument typu std::istream, zaś "z prawej strony", jeśli foo jest typu int, argument typu int&). Zgodnie z przyjętą w <iostream> konwencją, funkcja operator>> zwraca swój "lewy argument", czyli w naszym przypadku strumień std::cin. Następnie kompilator stwierdza, że zwrócony strumień jest typu std::istream i że występuje w kontekście boolowskim [tzn. powinien się dać przedstawić w postaci "prawda" lub "fałsz"; wymaga tego pętla while, która zostanie przerwana dla "fałszu" lub będzie kontynuowana dla "prawdy" - przyp. tłum]; w związku z tym strumień (typ std::istream) zostaje przekształcony do wartości boolowskiej.
Aby przekształcić strumień do wartości boolowskiej, kompilator wstawia wywołanie metody std::istream::operator void*(). Zwraca ona wskaźnik typu void*, który następnie jest konwertowany do wartości boolowskiej (NULL odpowiada false, pozostałe wartości wskaźnika odpowiadają true). Zatem w tym przypadku kompilator generuje wywołanie metody std::cin.operator void*(), dokładnie tak jakbyś jawnie przekształcił(a) strumień pisząc (void*) std::cin.
Operator przekształcenia operator void*() zwraca wskaźnik nie-NULL gdy strumień jest w "stanie prawidłowym" (ang. "good state"), lub wskaźnik NULL gdy strumień jest w "stanie błędu" (ang. "failed state"). Na przykład, jeśli wykonasz zbyt wiele odczytów (i w efekcie już dawno będziesz się znajdować na końcu pliku) lub gdy wartość odczytana ze strumienia nie będzie odpowiadać typowi obiektu foo (np., gdy foo jest typu int a odczytaną daną jest znak 'x'), strumień przejdzie w stan błędu i operator przekształcenia zwróci NULL.
Przyczyną dla której operator>> nie zwraca po prostu wartości bool (lub void*) celem poinformowania o wyniku operacji, jest możliwość zastosowania "kaskadowej" składni:
Łączność operatora >> jest lewostronna, stąd powyższy zapis można przedstawić tak:
Innymi słowy, gdyby operator>> zastąpić zwykłą funkcją o nazwie, powiedzmy, readFrom(), powyższe wyrażenie możnaby zapisać w ten sposób:
Jak zwykle, obliczanie wyrażenia rozpoczynamy od najbardziej wewnętrznego elementu. Z powodu lewostronnej łączności operatora >>, najbardziej wewnętrzne jest wyrażenie z lewej strony: std::cin >> foo. To wyrażenie zwraca strumień std::cin (ściślej, zwraca ono referencję do swojego lewostronnego argumentu), który zostaje umieszczony w następnym wyrażeniu [tym z "bar", w efekcie czego powstaje wyrażenie std::cin >> bar - przyp. tłum.]. To wyrażenie również zwróci (referencję do) std::cin, ale ta druga referencja zostanie zignorowana, gdyż jest ona wynikiem najbardziej zewnętrznego wyrażenia w instrukcji.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Ponieważ znacznik końca pliku (ang. "eof state") jest ustawiany dopiero przy próbie odczytania danych za końcem pliku. Oznacza to, że odczytanie ostatniego bajtu w pliku nie spowoduje jeszcze ustawienia znacznika "eof". Przykładowo, załóżmy że standardowe wejście jest połączone z klawiaturą w tym przypadku biblioteka C++ nie może nawet teoretycznie przewidzieć, czy znak, który użytkownik wpisał przed chwilą, jest ostatnim znakiem czy też może zostaną jeszcze wpisane kolejne.
Poniższy przykładowy kod będzie próbował odczytać jeden bajt tuż za końcem pliku - zmienna i osiągnie wartość o 1 większą, niż wynosi długość pliku liczona w bajtach:
Natomiast poniższy kod działa zgodnie z oczekiwaniami:
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

Ponieważ po odczytaniu liczby, wszystkie znaki wpisane po niej, a nie będące cyframi, pozostają w buforze wejściowym.
Jeśli Twój kod wygląda mniej więcej tak:
To tak naprawdę potrzebujesz czegoś takiego:
[Przypis tłumacza - Ostatnia instrukcja w pętli pomija wszystkie znaki w buforze wejściowym aż do napotkania znaku końca linii '\n' - ten znak również jest ignorowany. Normalnie pierwszy parametr metody "std::cin.ignore" oznacza maksymalną liczbę znaków do pominięcia - jeżeli jednak parametr ten będzie równy maksymalnej liczbie jaką można zapisać w typie int, czyli INT_MAX (lub "std::numeric_limits<int>::max()") to wtedy nie jest wprowadzane żadne ograniczenie co do liczby znaków i metoda po prostu pomija wszystkie znaki aż do napotkania takiego samego jak podano w drugim parametrze. Symbol INT_MAX zdefiniowany jest w nagłówkach <climits> i <limits.h> (wystarczy dołączyć jeden z nich).
Dzięki tej instrukcji unika się sytuacji, w której nieprawidłowa część danych z pola "Wiek:" jest przepisywana do pola "Imię:". Jeśli ktoś, na przykład, w polu "Wiek:" wpisze "20 i pół" to sekwencja " i pół" zostanie pominięta i nie trafi ona do pola "Imię:" w następnej iteracji]
Ponadto możesz oczywiście zmienić instrukcję for (;; na while (std::cin) [aby np. sprawdzać, czy podano wiek w nieprawidłowej formie - przyp. tłum.], ale nie należy tego mylić z pomijaniem zbędnych znaków na końcu pętli przy użyciu instrukcji: std::cin.ignore(...);.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

"std::endl" wysyła znak końca wiersza '\n' a następnie opróżnia bufor wyjściowy; oznacza to, że "std::endl" jest bardziej kosztowny pod względem wydajności. Oczywiście, jeżeli chcesz aby bufor był opróżniany dla każdej linijki, powinieneś (powinnaś) stosować "std::endl"; ale jeśli nie zależy Ci na opróżnianiu bufora przy każdej linijce, możesz przyśpieszyć swój kod stosując znaki '\n'.
Ten kod po prostu wysyła znak końca linii '\n':
Ten kod wysyła znak '\n', a następnie opróżnia bufor:
Ten kod po prostu opróżnia bufor:
Uwaga: każdy z powyższych trzech przykładów wymaga nagłówka #include <iostream>
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Możesz przeciążyć operator wysyłania <<, pisząc jego nową wersję zaprzyjaźnioną z klasą Fred.
Nową wersję operatora zdefiniowaliśmy w zwykłej funkcji (w naszym przypadku zaprzyjaźnionej z klasą Fred), ponieważ obiekt Fred jest prawostronnym argumentem operatora <<. Gdyby obiekty Fred miały być umieszczane po lewej stronie operatora << (to znaczy tak: myFred << std::cout, a nie tak: std::cout << myFred), moglibyśmy zdefiniować nową wersję operatora w postaci metody.
Zwróć uwagę, że operator<< zwraca strumień. Dzięki temu operacje wysyłania mogą być łączone kaskadowo.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Nie.
Tego rodzaju twierdzenia, że zawsze powinno się używać metod w stylu printOn() zamiast "zaprzyjaźnionych funkcji", przeważnie wynikają z niesłusznego przekonania, że "przyjaciele klas" naruszają enkapsulację i że wogóle nie powinno się ich stosować. Są to naiwne i złe przesądy: "przyjaciele klas", prawidłowo zastosowani, w rzeczywistości mogą nawet polepszyć enkapsulację.
Dla pełnej jasności tematu przedstawię o co chodzi we wspomnianej "metodzie w stylu printOn()". Pomysł polega na zdefiniowaniu wewnątrz klasy metody (na ogół nazywanej właśnie printOn()), która dokonywałaby właściwego wysyłania danych, a następnie zdefiniowaniu w zasięgu globalnym funkcji operator<< która wywoływałaby metodę printOn(). W złych rozwiązaniach, metoda printOn() jest publiczna, stąd operator<< nie musi być "przyjacielem klasy" może być zwykłą, globalną funkcją, nie będącą ani "przyjacielem" ani metodą klasy. Przykładowy kod mógłby wyglądać następująco:
Niektórzy niesłusznie zakładają, że takie rozwiązanie obniża koszty związane z utrzymywaniem kodu, "ponieważ pozwala ono uniknąć stosowania zaprzyjaźnionej funkcji". Jest to niesłuszne założenie, gdyż:
Podsumowując: rozwiązanie z metodą-wywoływaną-przez-globalną-funkcję wiąże się z pewnymi kosztami, ale nie przynosi żadnych korzyści. Dlatego jest to generalnie zły pomysł.
Notka: W sytuacji gdy metoda printOn() jest zadeklarowana jako chroniona lub prywatna, druga uwaga przestaje obowiązywać. Istnieją przypadki, gdy omówione rozwiązanie okazuje się być rozsądnym wyjściem, np. aby dodać obsługę wysyłania obiektów do strumienia dla całej hierarchii klas. Ponadto zwróć uwagę, że gdy metoda printOn() jest niepubliczna, konieczne jest zadeklarowanie funkcji operator<< jako "przyjaciela klasy".
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Możesz przeciążyć operator pobierania >>, pisząc jego nową wersję zaprzyjaźnioną z klasą Fred. Jest to rozwiązanie analogiczne jak w przypadku operatora wysyłania, z tym że parametr operatora nie jest zadeklarowany jako stały: deklaruje się go "Fred&" zamiast "const Fred&".
Zauważ, że operator>> zwraca strumień. Dzięki temu operacje pobierania mogą być łączone kaskadowo oraz/lub używane jako warunek w instrukcjach while i if.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Możesz napisać nową wersję operatora <<, zaprzyjaźnioną z klasą podstawową hierarchii. Wersja ta wywoływałaby chronioną, wirtualną metodę, która z kolei dokonywałaby właściwego wysłania obiektu:
W rezultacie operator<< zachowuje się tak, jakby był dynamicznie wiązany z odpowiednią klasą, pomimo tego, że jest to zwykła, zaprzyjaźniona funkcja. Technika ta nazywana jest Virtual Friend Function Idiom.
Zwróć uwagę, że klasy potomne przeciążają metodę printOn(std::ostream&) const. W szczególności, nie dostarczają one własnych wersji operatora <<.
Naturalnie, gdyby Base była abstrakcyjną klasą podstawową, metoda Base::printOn(std::ostream&) const mogłaby zostać zadeklarowana jako metoda abstrakcyjna przy użyciu zapisu "= 0".
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Zależy to od implementacji. Sprawdź w dokumentacji Twojego kompilatora.
Na przykład, załóżmy że chcesz dysponować binarnym wejściem/wyjściem przy użyciu strumieni std::cin i std::cout. Następnie załóżmy, że Twój system operacyjny (np. DOS lub OS/2) tłumaczy w strumieniu wejściowym sekwencje "\r\n" na "\n", zaś w strumieniu wyjściowym i strumieniu błędów, znaki "\n" na sekwencje "\r\n".
Niestety, nie istnieje żadne standardowe rozwiązanie pozwalająca na otwarcie strumieni std::cin, std::cout i/lub std::cerr w trybie binarnym. Zamknięcie strumieni i próba ich ponownego otwarcia w trybie binarnym może doprowadzić do nieprzewidzianych i niepożądanych skutków.
W systemach, w których istnieje różnica między strumieniem binarnym a strumieniem tekstowym, mogą istnieć rozwiązania pozwalające na zmianę trybu pracy strumienia - ale żeby coś się o nich dowiedzieć, będziesz musiał(a) poszukać informacji na ten temat w dokumentacji systemu.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Nie jest to standardową właściwością języka C++ C++ nie wymaga nawet, aby Twój system zawierał klawiaturę! Innymi słowy, każdy system operacyjny i producent stosują inne rozwiązania.
Proszę poszukać szczegółów dotyczących danej implementacji w dokumentacji dołączonej do Twojego kompilatora.
(Swoją drogą, w przypadku procesu pracującego pod kontrolą systemu UNIX na ogół wystarczą dwa kroki: najpierw trzeba ustawić terminal na tryb jednoznakowy [ang. single-character mode], a następnie wywołać funkcję select() lub poll() aby sprawdzić, czy klawisz został naciśnięty. Ewentualnie, możesz spróbować zastosować kod znajdujący się pod tym odsyłaczem.)
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Nie jest to standardową właściwością języka C++ C++ nie wymaga nawet, aby Twój system zawierał klawiaturę lub monitor. Oznacza to, że każdy system operacyjny oraz producent stosują inne rozwiązania.
Proszę poszukać szczegółów dotyczących danej implementacji w dokumentacji dołączonej do Twojego kompilatora.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Nie jest to standardową właściwością języka C++ C++ nie wymaga nawet, aby Twój system zawierał monitor. Oznacza to, że każdy system operacyjny oraz producent stosują inne rozwiązania.
Proszę poszukać szczegółów dotyczących danej implementacji w dokumentacji dołączonej do Twojego kompilatora.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

Nie jest to standardową właściwością języka C++ C++ nie wymaga nawet, aby Twój system zawierał monitor. Oznacza to, że każdy system operacyjny oraz producent stosują inne rozwiązania.
Proszę poszukać szczegółów dotyczących danej implementacji w dokumentacji dołączonej do Twojego kompilatora.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

Nie jest to standardową właściwością języka C++ C++ nie wymaga nawet, aby Twój system zawierał monitor. Oznacza to, że każdy system operacyjny oraz producent stosują inne rozwiązania.
Proszę poszukać szczegółów dotyczących danej implementacji w dokumentacji dołączonej do Twojego kompilatora.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Ponieważ "\t" oznacza "znak tabulacji".
Zamiast znaków backslash "\" powinieneś (powinnaś) używać znaków slash "/" (forward slash), nawet jeżeli pracujesz na systemie operacyjnym używającym backslashy (DOS, Windows, OS/2, etc.). Na przykład:
Zapamiętaj: backslash "\" używany jest w stałych napisowych do zapisywania znaków specjalnych: "\n" oznacza koniec wiersza, "\b" cofnięcie kursora o jedną pozycję i zmazanie znaku (backspace), "\t" poziomą tabulację, "\a" sygnał dźwiękowy, "\v" pionową tabulację itd. Dlatego nazwa "\version\next\alpha\beta\test.dat" interpretowana jest jako ciąg bardzo śmiesznych znaczków. Zamiast tego, bezpieczniej jest stosować nazwy plików w postaci "/version/next/alpha/beta/test.dat", nawet w przypadku systemów, w których znakiem oddzielającym nazwy kolejnych katalogów jest backslash "\". Jest to możliwe, gdyż procedury biblioteczne pracujące na tych systemach obsługują zarówno ścieżki ze znakami "/" jak i "\".
Oczywiście, możesz również zapisać nazwę pliku w postaci "\\version\\next\\alpha\\beta\\test.dat", [sekwencja "\\" zastępuje jeden znak "\" - przyp. tłum.] ale to może Cię kiedyś skrzywdzić (zawsze istnieje nie-zerowe prawdopodobieństwo, że w jakimś miejscu zapomnisz wpisać jednego znaku "\"; to dosyć subtelny błąd, którego wielu ludzi początkowo nie zauważa), poza tym nie pomoże Ci to w żadnym stopniu (stosowanie znaków "\\" nie ma żadnej przewagi nad stosowaniem znaków "/"). Ponadto, rozwiązanie ze slashami "/" jest bardziej przenośne, gdyż działa ono na wszystkich odmianach systemu Unix, Plan 9, Inferno, wszystkich wersjach Windows, OS/2, itd., natomiast rozwiązanie z backslashami "\\" działa tylko na nielicznych systemach z tej listy. Zatem stosując backslashe "\\" ponosisz jedynie koszty, nie otrzymując nic w zamian: zamiast nich stosuj znaki slash "/".
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Istnieją na to dwa proste sposoby: możesz skorzystać z funkcji zawartych w <cstdio> lub w bibliotece <iostream>. Generalnie, powinno się raczej stosować bibliotekę <iostream>.
Biblioteka <iostream> pozwala Ci przekonwertować niemal wszystkie typy danych na std::string przy użyciu poniższej składni (w tym przykładzie konwertowany jest typ double, ale możesz tu umieścić dowolny typ, który może być wysyłany do strumienia przy użyciu operatora <<):
Obiekt o klasy std::ostringstream udostępnia takie same mechanizmy służące do formatowania tekstu jak strumień std::cout. Możesz używać manipulatorów oraz flag formatujących aby wpływać na kształt końcowego ciągu znaków, dokładnie tak jak możesz to robić w przypadku std::cout.
W powyższym przykładzie wstawiamy wartość zmiennej x do o przy użyciu przeciążonego operatora wysyłania <<. Powoduje to wywołanie mechanizmów formatujących zaszytych w bibliotece iostream, które konwertują wartość zmiennej x do typu std::string. Instrukcja if sprawdza, czy konwersja przebiegła pomyślnie dla wbudowanych/prostych typów powinna się ona zawsze powieść, ale w dobrym stylu jest sprawdzanie tego na wszelki wypadek przy użyciu instrukcji if.
Wyrażenie o.str() zwraca napis typu std::string, który zawiera wszystko, co zostało wcześniej umieszczone w strumieniu o; w tym przypadku wartość zmiennej x przedstawioną w formie tekstowej.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Istnieją na to dwa proste sposoby: możesz użyć funkcji zadeklarowanych w nagłówku <cstdio> lub znajdujących się w bibliotece <iostream>. Generalnie, powinno się raczej korzystać z biblioteki <iostream>.
Biblioteka <iostream> umożliwia przekonwertowanie napisu typu std::string na niemal dowolny inny typ przy użyciu składni przedstawionej w poniższym przykładzie (w którym napis konwertowany jest na typ double, ale możesz tu umieścić cokolwiek co można odczytać przy użyciu operatora >>):
Obiekt i klasy std::istringstream udostępnia takie same mechanizmy służące do formatowania tekstu jak strumień std::cin. Możesz używać manipulatorów oraz flag formatujących dokładnie tak samo, jak w przypadku strumienia std::cin.
W powyższym przykładzie, inicjujemy strumień i klasy std::istringstream przy użyciu napisu s typu std::string (przykładowo, s może zawierać napis "123.456"), a następnie pobieramy liczbę ze strumienia i do zmiennej x przy użyciu przeciążonego operatora pobierania >>. W trakcie tej operacji zostają wywołane mechanizmy formatowania, które konwertują największą możliwą część napisu do typu zmiennej x.
Instrukcja if sprawdza, czy konwersja powiodła się czy też nie. Przykładowo, jeśli napis zawierałby znaki, które nie pasowałyby do typu zmiennej x, warunek w instrukcji if nie zostałby spełniony.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
E-mail the author
[ C++ FAQ Lite
| Spis treści
| Skorowidz
| O autorze
| ©
| Pobierz swoją własną kopię ]
Ostatnia aktualizacja Jun 17, 2002
Wersja polska: 0.1i Jul 12, 2004