Strumienie Bufrwanie danych Bufrwanie strumieni Dane znajdujące się w strumieniu należy interpretwać zgdnie z ich znaczeniem, ale zawsze należy pamiętać, że są t p prstu sekwencje bajtów. Za bsługę bajtwą danych strumieniwych dpwiedzialny jest biekt typu streambuf (dkładny typ zależy d teg, czy jest t strumień reprezentujący standardwe we/wy, plik, itd.) D teg biektu mżna sięgać bezpśredni: mżna np. przesunąć się kreślną liczbę bajtów bez ich frmatwania przez strumień 68 Bufrwanie strumieni Aby umżliwić sięgnięcie d biektu streambuf, każdy biekt istream zawiera metdę rdbuf(): basic_streambuf<elem, Traits> *rdbuf( ) cnst; zwraca wskaźnik d bufra strumieniweg. basic_streambuf<elem, Traits> *rdbuf( basic_streambuf<e, T> *_Sb); umieszcza bufr strumieniwy pdany w argumencie w miejsce pprzednieg, a wskaźnik d pprzednieg zwraca jak rezultat. Bufrwanie strumieni przykład: int main( ) { fstream file( "Test3.txt" ); streambuf *x = cut.rdbuf( file.rdbuf( ) ); // pdmiana.. cut << "test1" << endl; // dane są zapisywane d pliku cut.rdbuf(x); cut << "test2" << endl; ifstream in( "Test3.txt" ); cut << in.rdbuf(); // jednym ruchem cały plik! Okn terminala: test2 test1 69 70 Bufrwanie strumieni przykład: Aby przekazać wszystkie znaki z jedneg strumienia d drugieg napisaliśmy: cut << in.rdbuf(); Zastępuje t żmudne (i pdatne na błędy) dczytywanie klejnych znaków lub wierszy, jest też szybsze. Przypmnienie metda get: int_type get( ); basic_istream& get( char_type& _Ch ); basic_istream& get(char_type *_Str, streamsize _Cunt); basic_istream& get( char_type *_Str, streamsize _Cunt, char_type _Delim ); Przerywa swje działanie zaraz p znalezieniu granicznika, ale teg granicznika już nie pbiera ze strumienia. char c[10]; c[0] = cin.get( ); // pbiera jeden element cin.get( c[1] ); // pbiera jeden element cin.get( &c[2],3 );// pbiera d naptkania znaku \n, ale max. 3 cin.get( &c[4], 4, '7' );// pbiera d naptkania znaku 7, ale max. 4 cut << c << endl; 71 72 1
Bufrwanie strumieni Przykład: Zawartść pliku Test.txt: Istnieje również wersja metdy get, która ptrafi pisać bezpśredni d biektu streambuf (a nie tylk d C-napisu): basic_istream& get( basic_streambuf<elem, Tr> *_Strbuf); basic_istream& get( basic_streambuf<elem, Tr> *_Strbuf, char_type _Delim); ifstream in( "Test.txt" ); streambuf &sb = *cut.rdbuf(); while(!in.get(sb).ef()) cut << char(in.get()); in.get(sb) pbiera wiersz znaków d naptkania \n, ale teg znaku już nie pbiera i wysyła d sb Kiedy Kara Mustafa, wielki mistrz Krzyżaków, szedł z licznymi zastępy przez Alpy na Kraków, d brny swych psad zawsze będąc skry pbił g pd Grunwaldem król Stefan Batry. in.get() pbiera jeden znak Wada: Jeżeli w pliku trafi się pusta linia, in.get(sb) nic nie wypisze i zwróci błąd. 73 74 Przykład: ifstream in( "Test3.txt" ); streambuf &sb = *cut.rdbuf(); while(!in.get(sb).ef()) { if (in.fail()) // znalezin pusty wiersz in.clear(); // czyścimy flagę błędu cut << char(in.get()); in.get(sb) pbiera wiersz znaków d naptkania \n, ale teg znaku już nie pbiera i wysyła d sb Przeszukiwanie strumieni we/wy W każdym typie strumienia istream istnieje pjęcie następneg znaku d dczytania lub zapisania. Mżemy przesuwać się p znakach w strumieniu w przód i w tył, wskazując kreślne bezwzględne płżenie alb przesunięcie względem płżenia bieżąceg. Określanie płżenia: ps_type tellp( ); ps_type tellg( ); (d użycia z stream) (d użycia z istream) in.get() pbiera jeden znak 75 76 Przykład: ifstream file; char c; Przesunięcie się w strumieniu: basic_stream& seekp( ps_type _Ps ); basic_istream& seekg( ps_type _Ps ); (d użycia z stream) (d użycia z istream) file.pen( "basic_istream_tellg.txt" ); i = file.tellg( ); file >> c; cut << c << " " << i << endl; // pjawi się znak i 0 i = file.tellg( ); file >> c; cut << c << " " << i << endl; // pjawi się znak i 1 Metdy jednargumentwe pzwalają przesunąć się w kreślne miejsce: fstream x( "itest.txt" ); int i = x.tellp( ); // zwróci 0 cut << i << endl; x << "1234567890"; i = x.tellp( ); // zwróci 10 cut << i << endl; x.seekp( i-5 ); x << " "; // gdzie zstanie umieszczny znak spacji? 77 78 2
Przesunięcie się w strumieniu: basic_stream& seekp( ff_type _Off, is_base::seekdir _Way ); basic_istream& seekg( ff_type _Off, is_base::seekdir _Way ); Metdy dwuargumentwe pzwalają przesunąć się kreślny ffset względem pczątku, kńca lub bieżąceg płżenia: is::beg d pczątku (w ANSI C stdi: SEEK_SET) is::cur d aktualneg płżenia (w ANSI C stdi: SEEK_CUR) is::end d kńca strumienia (w ANSI C stdi: SEEK_END) Wartść przesunięcia jak liczba ddatnia przesuwa w praw a ujemna w lew. Przesunięcie się w strumieniu przykład: fstream x( "itest.txt" ); int i = x.tellp( ); // pbieramy aktualne płżenie w pliku cut << i << endl; x << "1234567890"; i = x.tellp( ); // pbieramy aktualne płżenie w pliku cut << i << endl; x.seekp( i - 5 ); // zmieniamy aktualne płżenie w pliku x << " "; x.seekp( -2, is::end ); // jeszcze raz zmieniamy.. x << "#"; // P wyknaniu kdu plik zawiera: 12345 78#0 79 80 ifstream file; char c, c1; file.pen( test.txt" ); file.seekg(2); file >> c; cut << c << endl; file.seekg( 0, is_base::beg); file >> c; cut << c << endl; file.seekg( -1, is_base::end); file >> c1; cut << c1 << endl; Plik wejściwy test.txt: 0123456789 W knie knsli: 2 0 9 Twrzenie biektu strumienia umżliwiająceg pisanie i czytanie ifstream in2("test.txt", is::in is::ut); stream ut2(in2.rdbuf()); cut << in2.rdbuf() << endl; ut2 << "ile Rman ładny dyndał na mreli"; ut2.seekp(0, is::beg); ut2 << "jeż leje lwa paw leje lżej"; in2.seekg(0, is::beg); cut << in2.rdbuf() << endl; Plik wejściwy test.txt:!!!!!@@@@@#####$$$$$%%%%%----- W knie knsli:!!!!!@@@@@#####$$$$$%%%%%----- jeż leje lwa, paw leje lżej---ile Rman ładny dyndał na mreli? Dlaczeg tekst pierwszy ddawany jest na kńcu pliku? 81 82 Strumienie pwiązane z łańcuchami <sstream> Łańcuchwe strumienie wejściwe #include <cassert> #include <cmath> // fabs() #include <limits> // epsiln() #include <sstream> #include <string> int main( ) { istringstream s("47 1.414 T tylk taki test"); duble f; s >> i >> f; assert(i == 47); duble relerr = (fabs(f) - 1.414) / 1.414; assert(relerr <= numeric_limits<duble>::epsiln()); 83 84 3
Łańcuchwe strumienie wejściwe int main( ) { istringstream s("47 1.414 T tylk taki test"); duble f; s >> i >> f; assert(i == 47); duble relerr = (fabs(f) - 1.414) / 1.414; assert(relerr <= numeric_limits<duble>::epsiln()); string buf2; s >> buf2; assert(buf2 == "T"); cut << s.rdbuf() << endl; // " tylk taki test" Łańcuchwe strumienie wejściwe Pbieranie wartści d zmiennych dbywa się zgdnie z ich typem: istringstream s("47 1.414 T tylk taki test"); duble f; s >> i >> f; // i=47 f = 1.414 Gdyby zmienić klejnść liczb w ciągu: istringstream s("1.414 47 T tylk taki test"); wtedy: s >> i >> f; // i=1 f = 0.414 85 86 Łańcuchwe strumienie wyjściwe cut << "napisz wartści int, flat i string: "; flat f; cin >> i >> f; cin >> ws; // drzuć ewentualne białe znaki string stuff; getline(cin, stuff); // pbierz d kńca wiersza stringstream s; s << "integer = " << i << endl; s << "flat = " << f << endl; s << "string = " << stuff << endl; string result = s.str(); cut << result << endl; Użycie dwukierunkweg strumienia łańcuchweg string text = "W Mskwie na Placu Czerwnym"; stringstream ss(text); ss.seekp(0, is::end); ss << " rzdają samchdy."; assert(ss.str() == "W Mskwie na Placu Czerwnym rzdają samchdy."); ss.seekg(18, is::beg); string wrd; ss >> wrd; assert(wrd == "Czerwnym"); ss.seekp(2, is::beg); ss << "Kijwie"; ss.seekg(29, is::beg); ss >> wrd; assert(wrd == "rzdają"); ss.seekp(37, is::beg); ss << "rwery.."; cut << ss.str() << endl; 87 88 Binarny dstęp d danych: D dczytu danych ze strumienia w taki spsób, aby mżna je był interpretwać jak ciąg binarny! służy: basic_istream& read( char_type *_Str, streamsize _Cunt ); Funkcja dczytuje kreślną liczbę bajtów _Cunt i zapisuje ją pd wskazany adres wskaźnik _Str. Jeżeli wcześniej naptka kniec pliku, przerywa dczyt i kńczy działanie ustawiając dpwiednią flagę błędu. Strumienie Binarny dstęp d danych Ciąg binarny ciąg bajtów (nie bitów!). Interpretacja danych dwlnych typów jak ciąg binarny plega na dczytaniu tych danych z pminięciem infrmacji ich strukturze, tj. bajt p bajcie (nie bit p bicie!). 90 4
Binarny dstęp d danych: D zapisu danych d strumienia służy: basic_stream& write( cnst char_type *_Str, streamsize _Cunt ); Aby zastswać tę funkcję d krzystania z pliku w trybie binarnym, należy bszary pamięci, d który dane mają trafić (lub skąd mają być pbrane), zrzutwać na wskaźniki typu cnst char_type* (zapis d strumienia) lub char_type* (dczyt ze strumienia). Binarny dstęp d danych: struct Blczek { int a; duble b; char c; ; int main( ) { fstream fx( "test.txt", is::binary); Blczek s={3,3.14,'a'; cnst char *pm = (cnst char*)&s; fx.write((char*)&s, sizef(s)); fx.write(pm, sizef(s)); fx.clse(); ifstream ifx( "test.txt",is::binary); Blczek s2; char *pm2 = (char*)&s2; ifx.read((char*)&s2, sizef(s2)); ifx.read(pm2, sizef(s2)); cut << s2.a << s2.b << s2.c; 91 92 Wprwadzenie Krzystanie z wyjątków pzwala ddzielić kd bsługi błędu d kdu wyknania zadania Zamiast umieszczać w kdzie bsługi zadania sekwencje instrukcji dpwiedzialne za pprawne zachwanie prgramu w razie wystąpienia błędu, t piszemy kd bsługi zadania tak, jakbyśmy zakładali, że wszystk dbędzie się bez prblemów. Kd bsługi błędu umieszczamy natmiast w innym miejscu prgramu i tylk wiążemy razem te dwa blki kdu współzależnścią. 94 Wprwadzenie Wystąpienie błędu pwduje natychmiastwe przerwanie bsługi zadania i przekazanie sterwania d inneg bszaru kdu, gdzie znajdują się instrukcje bsługujące błąd. Kd bsługi zadania mżna pdzielić na kilka sekcji, które uważamy za funkcjnalnie zamknięte całści. Taka sekcja mże w całści wyknać się pprawnie lub w całści zstać niewyknana Wprwadzenie Aby mechanizm wyjątków mógł działać adekwatnie d sytuacji błędnej, musi zstać przekazana infrmacja rdzaju błędu, jaki wystąpił. D teg celu służą biekty, w których mżna zawrzeć infrmacje indywidualnych klicznściach zdarzenia. 95 96 5
Wprwadzenie Pdsumwując, d wprwadzenia wyjątków ptrzebne są: 1) spsób znakwania sekcji kdu, której wiadm, że w trakcie jej wyknania mże być rzucny wyjątek, 2) spsób znakwania sekcji kdu, która nie jest zwykłym fragmentem prgramu, ale reprezentuje kd mający się wyknać w przypadku rzucenia wyjątku, 3) instrukcja rzucania wyjątku, która spwduje przeniesienie sterwania, 4) klasa na pdstawie której twrzne będą biekty zawierające infrmacje klicznściach, w których wystąpił błąd. Rzucanie wyjątków instrukcja thrw Pwduje: 1) przerwanie wyknywania bieżąceg kdu, 2) wygenerwanie biektu przechwująceg infrmacje klicznściach błędu, 3) przeniesienie sterwania z bieżąceg kdu d miejsca, które jest w stanie przyjąć biekt daneg typu i wykrzystać zawarte w nim infrmacje d bsługi sytuacji błędnej. 97 98 Rzucanie wyjątków Jeżeli w jakimś miejscu kdu wystąpiła sytuacja niepprawna, t zamiast ustawiać flagę błędu, lub natychmiast kńczyć działanie funkcji pleceniem return, które zwraca kd błędu, w tym miejscu umieszczamy plecenie rzucenia wyjątku, np.: if (x>0) y = sqrt(x); else return -1; // stary spsób, typwy dla języka C // alb: thrw MyErrr("nieprawidłwa próba pierwiastkwania"); // nwy spsób, typwy dla C++. // MyErrr t nazwa klasy, której biekt twrzymy C się stanie dalej?.. 1) Jeżeli jesteśmy wewnątrz funkcji, thrw spwduje przerwanie wyknania tej funkcji i przejście sterwania d miejsca, gdzie funkcja była wywłana. 2) Jeżeli była wywłana wewnątrz innej funkcji, ta również zstanie przerwana, itd. Przerwania i przejścia sterwania w górę będą następwały aż djdą d funkcji main 3) Funkcja main również zstanie przerwana a prgram zakńczy swje działanie wyświetlając kmunikat, że zstał przerwany z pwdu wyrzucenia wyjątku. T bardz niedsknała bsługa sytuacji błędnej 99 100 Aby prgram nie przerwał swjeg działania, należy w nim zawrzeć ddatkwe instrukcje pzwalające na przechwycenie wyjątku w prgramie, tj.: Należy 1) pinfrmwać kmpilatr, w jakim bszarze kdu prgramu któraklwiek z instrukcji teg kdu mże spwdwać rzucenie wyjątku, raz 2) umieścić pd tym bszarem instrukcje przechwytujące. D znakwania sekcji kdu, w której mże wystąpić wyjątek, służy instrukcja try Przykład:. // tutaj różne instrukcje naszeg prgramu Jeżeli w ciele funkcji znajduje się blk try, wewnątrz któreg zstał rzucny wyjątek, funkcja nie zstanie przerwana, tj. sterwanie nie puści tej funkcji, ale zstanie przeniesine tylk na kniec teg blku try, d pierwszej instrukcji za tym blkiem. 101 102 6
Raz rzucny wyjątek musi gdzieś trafić. Jeżeli prgramista nie przewidział miejsca bsługi, prgram zstanie zatrzymany a wyjątek bsłużny przez system peracyjny. Nrmalnym miejscem bsługi wyjątku jest prcedura, umieszczna zaraz za blkiem try Dlateg bezpśredni za kńcem teg blku, jeżeli chcemy, mżemy umieścić kawałek kdu, który wykna dpwiednie czynnści, właściwe dla klicznści, w których zaszła sytuacja błędna. Taka czynnść nazywana jest bsługą wyjątku. prcedura ma nazwę catch i jeden argument wywłania, którym jest biekt kreślneg typu zgdneg z typem biektu reprezentująceg wyjątek: // Kd, który mże generwać wyjątki catch (typ1 id1) { // bsługa wyjątku typu typ1 catch (typ2 id2) { // bsługa wyjątku typu typ2 103 104 class Pdgladanie { Pdgladanie() { cut << "Pdgladanie()"<< endl; ~Pdgladanie() { cut << "~Pdgladanie()"<< endl; ; vid przezdziurkeodklucza() { Pdgladanie P; fr(int i = 0; i < 3; i++) cut <<"Nic nie widzę."<< endl; thrw 47; cut << I c? widzisz cś?..." << endl; przezdziurkeodklucza(); catch(int) { cut << T daj też ppatrzeć." << endl; W którym mmencie zstanie wyknany destruktr klasy Pdgladanie? Aby dpaswać wyjątek d prcedury bsługi, nie musi istnieć dkładna zgdnść wyjątku i prcedury g bsługującej. Obiekt (lub referencja d biektu) klasy pchdnej pasują d prcedury dtyczącej klasy bazwej. W przypadku biektu dchdzi jedynie d bcięcia biektu d typu bazweg składwe ddane w klasie pchdnej nie są dstępne Z teg pwdu, a także aby uniknąć kpiwania biektów lepiej jest używać referencji d biektów. 105 106 Jeżeli.. w ciele funkcji żaden z typów zadeklarwanych we frazach catch nie pasuje d rzucneg biektu-wyjątku t.. sterwanie przensi się d miejsca, gdzie funkcja zstała wywłana i tam szuka sekcji try-catch. A jeżeli takiej tam nie ma, lub typy wyjątków we frazach catch nie pasują, t sterwanie dalej wędruje w górę w hierarchii wywłań funkcji w pszukiwaniu frazy catch z pasującym typem danych Sekcje try-catch mgą być zagnieżdżne dpwiedni d hierarchii wywłań funkcji. class Brzskwinia { class Pestka {; class Mala : public Pestka {; class Duza : public Pestka {; vid scisk() { thrw Duza(); ; Brzskwinia b; b.scisk(); catch(brzskwinia::mala&) { // nigdy nie zstanie wywłany.. cut << "Mala Pestka schwytana" << endl; catch(brzskwinia::duza&) { //.. a ten tak. cut << "Duza Pestka schwytana" << endl; catch(brzskwinia::pestka&) { // ten jest tylk pr frma. cut << " Pestka schwytana" << endl; ; 107 108 7
Cytat: An exceptin in C++ (Java and C# are similar) is a way t put a message in a bttle at sme pint in a prgram, abandn ship, and hpe that smene is lking fr yur message smewhere dwn the call stack. C++ Ckbk by D. Ryan Stephens; Jeff Cgswell; Jnathan Turkanis; Christpher Diggins Przechwytywanie wyjątków dwlneg typu plega na nie wskazywaniu żadneg typu w argumencie frazy catch catch( ) { cut << złapałem, c akurat leciał.. << endl; Użycie wielkrpka pwduje przechwytywanie wszelkich wyjątków, dlateg taka fraza pwinna być statnią w sekwencji fraz catch, aby nie przechwytywać wyjątków przeznacznych dla fraz, które znalazłyby się za nią. 109 110 class Brzskwinia { class Pestka {; class Mala : public Pestka {; class Duza : public Pestka {; vid scisk() { thrw Duza(); ; Brzskwinia b; b.scisk(); catch(brzskwinia::mala&) { // nigdy nie zstanie wywłany.. cut << "Mala Pestka schwytana" << endl; catch(brzskwinia::duza&) { //.. a ten tak. cut << "Duza Pestka schwytana" << endl; catch(...) { // ten lapie wszystk. cut << "zlapalem, c akurat lecial.." << endl; ; Mże zdarzyć się, że w danym miejscu kdu musimy: 1. bsłużyć błędną sytuację w kntekście lkalnym raz 2. dknać przerwania działania całej większej części kdu i 3. dknać ddatkwej bsługi tej błędnej sytuacji na wyższym pzimie wywłań. Np., kiedy pdając w argumencie frazy catch wielkrpek złapaliśmy wyjątek dwlneg typu tylk p t, aby np. zamknąć pewne zasby, które są kntrlwane przez biekty lkalne, c jednak nie znacza, że na tym pwinna się zakńczyć bsługa teg błędu. 111 112 W takiej sytuacji ptrzebujemy 1. przechwycić wyjątek, 2. zrealizwać stswną bsługę, 3. puścić wyjątek dalej. Pnwne wyrzucenie wyjątku następuje w chwili wywłania thrw bez żadnych argumentów w prcedurze bsługi wyjątku. catch( ) { cut << złapałem, c leciał i na wszelki wypadek zwalniam c zająłem.. << endl;.. // tu następuje zwlnienie zajętych zasbów thrw; Wszelkie dalsze frazy catch teg sameg blku try są ignrwane. Instrukcja thrw; pwduje, że wyjątek przechdzi d prcedur bsługi wyjątków następneg wyższeg pzimu, a cały biekt reprezentujący wyjątek pzstaje niezmieniny aby prcedura wyższeg pzimu mgła pzyskać z nieg wszelkie zawarte w nim infrmacje 113 114 8
Jeżeli żadna fraza catch nie pasuje d wyjątku, przechdzi n na wyższy pzim sterwania: d funkcji lub blku try taczająceg bieżący try, który nie przechwycił wyjątku. Jeżeli wyjątek nie zstanie przechwycny na żadnym pzimie, autmatycznie wywływana jest specjalna funkcja bibliteczna terminate(). Dmyślnie terminate() wywłuje funkcję abrt() ze standardwej bibliteki C. Kiedy wywływana jest funkcja abrt(), nie są wywływane żadne funkcje nrmalnie uruchamiane przy zamykaniu prgramu, a więc destruktry biektów glbalnych i statycznych. Funkcję terminate() mżna pdmienić za pmcą funkcji set_terminate(). Zdefiniwana przez użytkwnika funkcja musi być bezargumentwa i zwracać vid. Nie mże rzucać wyjątków: #include<exceptin> #include<istream> using namespace std; vid zaknczenie( ) { // nwa funkcja cut << "Ja tu wrce.." << endl; abrt( ); ; int main( ) { terminate_handler stary_uchwyt = set_terminate(zaknczenie); thrw bad_allc( ); // sygnalizuje niepwdzenie alkacji pamięci ; 115 116 vid nwe_zaknczenie( ) { cut << "Ja tu wrce.." << endl; exit(0); ; vid (*wf)() = set_terminate(nwe_zaknczenie); class Fuszerka { class Owc {; vid f() { cut << "Fuszerka ::f()" << endl; thrw Owc(); ; Fuszerka() { ; ~Fuszerka() { ; ; Fuszerka b; b.f(); catch(...) { cut << "zlapalem cs.." << endl; W knie terminala: Fuszerka ::f() zlapalem cs.. vid nwe_zaknczenie( ) { cut << "Ja tu wrce.." << endl; exit(0); ; vid (*wf)() = set_terminate(nwe_zaknczenie); class Fuszerka { class Owc {; vid f() { cut << "Fuszerka ::f()" << endl; thrw Owc(); ; Fuszerka() { ; ~Fuszerka() {thrw 'c'; ; ; Fuszerka b; b.f(); catch(...) { cut << "zlapalem cs.." << endl; W knie terminala: Fuszerka ::f() Ja tu wrce.. 117 118 Kiedy w trakcie wyknywania funkcji zstanie rzucny wyjątek, mże dla pewneg zbiru zmiennych nastąpić wyjście z zasięgu, w którym zstały zadeklarwane (np. wyjście z funkcji, w której były zadeklarwane zmienne lkalne). Obsługa wyjątków w C++ gwarantuje, że przy wychdzeniu z zasięgu dla wszystkich zmiennych teg zasięgu, których knstruktry zstały wywłane d kńca, zstaną też wywłane destruktry. 119 class Trace { static int cunter; int bjid; Trace() { bjid = cunter++; cut << knstruktr Trace #" << bjid << endl; if(bjid == 3) thrw 3; ~Trace() { cut << destruktr Trace #" << bjid << endl; ; int Trace::cunter = 0; Trace DynamicznySts[5]; catch(int i) { cut << przechwycn " << i << endl; W knie terminala: knstruktr Trace #0 knstruktr Trace #1 knstruktr Trace #2 knstruktr Trace #3 destruktr Trace #2 destruktr Trace #1 destruktr Trace #0 przechwycn 3 120 9