C++ - szablony. C++ - szablony. C++ - szablony. C++ - szablony SZABLONY SZABLONY FUNKCJI

Podobne dokumenty
C++ - szablony. C++ - szablony. C++ - szablony. C++ - szablony. C++ - szablony. C++ - szablony

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

C++ - strumienie. C++ - strumienie. C++ - strumienie. C++ - strumienie. C++ - strumienie STRUMIENIE

C++ - strumienie. C++ - strumienie. C++ - strumienie. C++ - strumienie. C++ - strumienie STRUMIENIE

Operacje wejścia/wyjścia (odsłona druga) - pliki

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 21 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 24

C++ - [3-5] Pliki i strumienie w C++

C++ - szablony. C++ - szablony. C++ - strumienie. C++ - strumienie. C++ - strumienie STRUMIENIE

Wejście wyjście strumieniowe

Część 4 życie programu

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Program dopisujący gwiazdkę na końcu pliku tekstowego o nazwie podanej przez uŝytkownika oraz wypisujący zawartość tego pliku.

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Język C++ wykład VIII

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 12. Karol Tarnowski A-1 p.

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

Język C++ Różnice między C a C++

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 5. Karol Tarnowski A-1 p.

Wykład 2 Operacje wejściawyjścia. Ewa Gajda

INFORMATYKA Studia Niestacjonarne Elektrotechnika

Programowanie w C++ Wykład 9. Katarzyna Grzelak. 14 maja K.Grzelak (Wykład 9) Programowanie w C++ 1 / 30

Podstawy programowania skrót z wykładów:

Operacje na plikach. Informatyka. Standardowe strumienie wejścia i wyjścia

Pliki wykład. Dorota Pylak

Pliki wykład 2. Dorota Pylak

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

1 Pierwsze kroki w C++ cz.3 2 Obsługa plików

Szablony. Szablony funkcji

PARADYGMATY PROGRAMOWANIA Wykład 4

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 20 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 32

Wstęp do programowania obiektowego. Przekazywanie parametrów do funkcji w C++ Metody i funkcje operatorowe Strumienie: standardowe, plikowe, napisowe

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

Pliki wykład. Dorota Pylak

Pliki wykład 2 -przekazywanie strumieni do funkcji -funkcje get(char &) i getline(string)

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Wstęp do Programowania, laboratorium 02

Szablony klas, zastosowanie szablonów w programach

Operacje wejścia/wyjścia odsłona pierwsza

I - Microsoft Visual Studio C++

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

Programowanie w językach

Języki i metodyka programowania. Wprowadzenie do języka C

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

TEMAT : KLASY DZIEDZICZENIE

jest mocny, skoro da się w nim wyrazić nowe pojęcia; łatwiej przenieść go na nową platformę jest mniejszy.

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

1 Podstawy c++ w pigułce.

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Wykład 8: klasy cz. 4

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

Programowanie komputerowe. Zajęcia 1

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Programowanie w C++ Wykład 13. Katarzyna Grzelak. 4 czerwca K.Grzelak (Wykład 13) Programowanie w C++ 1 / 26

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

4. Wyrzuć wyjątek jeśli zmienna ist nie istnieje bloki: try, catch i wyrzucanie wyjątku

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Techniki programowania INP001002Wl rok akademicki 2017/18 semestr letni. Wykład 5. Karol Tarnowski A-1 p.

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Podział programu na moduły

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Pliki wykład 2. Dorota Pylak

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Wstęp do programowania. Wykład 1

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

2 Przygotował: mgr inż. Maciej Lasota

PROE wykład 4 pozostałe operatory, forward declaration, dziedziczenie. dr inż. Jacek Naruniec

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Szablony funkcji i szablony klas

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Abstrakcyjny typ danych

Podstawy Programowania

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Programowanie Obiektowe i C++

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Mechanizm dziedziczenia

Wstęp do programowania

Dziedziczenie & W slajdach są materiały zapożyczone z

Zajęcia nr 5 Algorytmy i wskaźniki. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Ok. Rozbijmy to na czynniki pierwsze, pomijając fragmenty, które już znamy:

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 5

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

Transkrypt:

SZABLONY Szablony, nazywane też wzorcami, pozwalają na abstrakcyjne definiowanie funkcji i klas w terminach parametrów, którymi są typy danych Na podstawie szablonu kompilator sam może wygenerować wiele definicji konkretnych funkcji i klas, kiedy będą potrzebne Standardowa biblioteka C++ bazuje na szablonach i dlatego jest nazywana STL: Standard Template Library 271 część 1: SZABLONY FUNKCJI Szablony funkcji Zdarza się, że wiele funkcji jest do siebie bardzo podobnych w działaniu różnią się tylko typem argumentu, który przyjmują Np. funkcja która z tablicy wybiera element o najmniejszej wartości: algorytm szukania jest wspólny dla większości typów danych. Jednak jeżeli chcemy znajdować najmniejszą wartość dla tablic z elementami typu: int, double, lub Integer (zdefiniowana klasa, dla której istnieje przeciążony operator porównania), to dla każdego z typów należy napisać na nowo całą funkcję Jeżeli te funkcje będą fragmentem biblioteki, to za każdym razem kiedy będziemy potrzebowali dopisać funkcję dla nowego typu danych w tablicy, będzie trzeba rekompilować bibliotekę i udostępniać jej użytkownikom nowy release To jest uciążliwe. Lepiej zrobić tę funkcję raz wykorzystując szablony. 273 Kompilator tworzy tyle wersji funkcji wg wskazanego szablonu, ile programista zażąda w swoim programie: będą takie same co do działania, ale będą różniły się co do typów argumentów wywołania, typów zmiennych lokalnych czy typu wartości zwracanej Można je generować nawet dla typów, które w chwili pisania szablonu jeszcze w ogóle nie istniały Aby poinformować kompilator, że kod, który piszemy reprezentuje szablon, należy użyć słowa kluczowego template - kod, który znajduje się poniżej tego słowa jest definicją wzorca funkcji lub klasy Przykład: template <class T1, class T2> T2& moja_funkcja(t1 arg1, T2* arg2) // Tutaj jakiś kod funkcji // wykorzystującej typy T1 i T2 return *arg2; W nawiasach kątowych lista parametrów formalnych wzorca, każdy poprzedzony słowem class, lub typename (typename wprowadzono później) Parametry zwyczajowo nazywa się z wykorzystaniem T, np. T1, T2 Następnie występuje definicja wzorca wykorzystująca T1 i T2 tam, gdzie powinna występować nazwa konkretnego typu 274 275 1

Na podstawie typów argumentów kompilator ma zgadnąć, który szablon i jak należy wykorzystać: template <class T1, class T2> T2& moja_funkcja(t1 arg1, T2* arg2) return *arg2; int main(int argc, char *argv[]) int a = 9; double b = 10.2, c = 3.14; moja_funkcja(a, &b); // konkretyzacja wzorca na // double& moja_funkcja(int arg1, double* arg2) moja_funkcja(a, &c); // tu nie ma konkretyzacji funkcja już jest. moja_funkcja(b, &a); // konkretyzacja wzorca na //int& moja_funkcja(double arg1, int* arg2) Słowo kluczowe class w linii: template <class T1, class T2> nie oznacza, że T1 i T2 mogą być tylko klasami (jak widać na przykładzie); mogą to być dowolne typy (wbudowane lub definiowane przez użytkownika) Dla czytelności w późniejszych wersjach kompilatorów zastąpiono to słowo słowem typename template < typename T1, typename T2> 276 277 Przeciążanie szablonów funkcji: template <typename T> T wiekszy(const T& k1, const T& k2) return k1 < k2? k2 : k1 ; double wiekszy(const double& d1, const double& d2) return d1 < d2? d2 : d1 ; int main(int argc, char *argv[]) int a, b=0, c=1; a = wiekszy(b,c); // która wersja funkcji zostanie użyta? // Istnieje standardowa konwersja int na double, ale // istnieje też szablon wg którego można utworzyć // właściwą funkcję double x, y=0.3, z=2.14; x = wiekszy(y,z); // która wersja funkcji zostanie użyta? Wskazywanie wartości parametru szablonu class Integer int i; public: Integer(int ii): i(ii) ; bool operator<( const Integer& rv) const return i < rv.i; ; bool operator>( const Integer& rv) const return i > rv.i; ; template <typename T> T wiekszy(const T& k1, const T& k2) return k1 < k2? k2 : k1 ; int main(int argc, char *argv[]) Integer I1(0), I2(1), I3(2); I1 = wiekszy<integer>(i2, I3); Jeżeli nie ma jednoznacznej interpretacji, można za nazwą szablonu w nawiasach kątowych podać listę typów, które należy w tym miejscu zastosować 278 ; 279 Informacja o typie zmiennej Testując szablony czasem niezbędne jest sprawdzenie, jakiego typu są zmienne, których szablon używa (czy np. kompilator sam podjął decyzję o konwersji zmiennej na podobny typ, etc) Do sprawdzenia służy operator typeid Operator zwraca obiekt typu const std::type_info Klasa ta ma pożyteczną metodę name() Chciałoby się napisać: std::type_info typ = typeid(a); printf("%s\n",typ.name()); Jednak autorzy biblioteki sprytnie zastrzegli konstruktor kopiujący oraz operator przypisania jako private uniemożliwiając de facto tworzenie w programie własnych obiektów tego typu z obiektów zwracanych przez operator typeid: private: /// Assigning type_info is not supported. Made private. type_info& operator=(const type_info&); type_info(const type_info&); Dlatego aby wywołać metodę name można wyłącznie napisać tak: printf("%s\n",typeid(k1).name()); 280 281 2

Porada programistyczna Na etapie testowania programu można w szablonie funkcji i w funkcji dodać instrukcje ujawniające typ danych, które są przetwarzane: template <typename T> T wiekszy(const T& k1, const T& k2) printf("szablon dla: %s\n",typeid(k1).name()); return k1 < k2? k2 : k1 ; double wiekszy(const double& d1, const double& d2) printf("dla double: %s\n",typeid(d1).name()); return d1 < d2? d2 : d1 ; część 2: SZABLONY KLAS 282 Szablony klas Na tej samej zasadzie co szablony funkcji można tworzyć szablony klas Składnia jest analogiczna: template <typename T, typename M> class Klasa // tu używamy typów T i M ; Aby utworzyć obiekt nie możemy napisać: Klasa K; bo nie wiadomo, jakie typy przypisać parametrom M i T, a kompilator nie ma szansy domyślić się. Wskazanie wartości parametrów jest konieczne. Szablony klas Deklaracja obiektów: template <typename T, typename M> class Klasa // tu używamy typów T i M ; Klasa<double, int> K; Klasa<int, Integer> I; 284 285 Szablony klas Jeżeli chcemy, żeby metody tej klasy zostały definiowane poza szablonem klasy, to musimy tam prawidłowo napisać nazwę szablonu: Klasa::Klasa(); // konstruktor domyślny Źle! template <typename T, typename M> Klasa<T, M>::Klasa(); // kontruktor domyślny Dobrze! Parametry wzorca klasy mogą być też wartościami określonego typu, np. : template <typename T, int rozmiar > class Klasa // tu używamy typu T i zmiennej rozmiar ; Klasa<int, 100> Tab; Klasa<double, 2000> *Tab2; Klasa<double, 3000> *Tab3; Tab, Tab2 i Tab3 są obiektami różnych typów, dlatego kompilator nie zaakceptuje takich instrukcji jak przepisanie wartości między wskaźnikami, itp. 286 287 3

Projekt składający się z pliku main.cpp i biblioteki (para plików:.cpp i.h) main.cpp #include wyklad13b.h" Tablica<int, 100> Tab1; wyklad13b.h template <typename T, int rozmiar> class Tablica wyklad13b.cpp #include wyklad13b.h" 288 Szablony vs. pliki nagłówkowe Zasada: deklaracje i kompletne definicje szablonów funkcji i klas umieszczamy w pliku nagłówkowym (i tylko tam). Uzasadnienie: Skoro szablony są wykorzystywane do generowania kodu tylko gdy w kodzie wystąpi wykorzystanie szablonu, to: 1. Umieszczenie kodu metod i funkcji w pliku cpp sprawi, że będzie on kompilowany oddzielnie nie będzie w tym pliku instrukcji wykorzystujących szablon, które należą do innych funkcji i metod, a więc kompilator nie będzie wiedział jakich konkretyzacji dokonać 2. Tam, gdzie będą próby wykorzystania szablonu, kompilator będzie miał same nagłówki klas i funkcji, ale bez definicji, więc nie będzie potrafił skonkretyzować żądanych wersji szablonu Efekt uboczny: kod szablonów jest jawny dla każdego użytkownika naszej biblioteki (pliki nagłówkowe są dostarczane zawsze w postaci źródłowej) 289 Czasem w plikach C++ zakładanych automatycznie używana jest dyrektywa using: using namespace std; Po co? PRZESTRZENIE NAZW I STANDARDOWE BIBLIOTEKI Ponieważ w C++ wszystko, co programista utworzy i wszystko, co zawarte jest w bibliotekach funkcjonuje w jakiejś przestrzeni nazw. A my najczęściej chcemy używać tego, co należy do przestrzeni nazw std Co to jest przestrzeń nazw? 291 Przestrzenie nazw pozwalają na podział kodu na logiczne jednostki. Stąd przestrzeń nazw to fragment kodu programu Wewnątrz jednej przestrzeni nazw wszystkie funkcje i klasy powinny logicznie uzupełniać się tworząc całość skierowaną na realizacje określonego celu, np. kompletną implementację złożonego algorytmu, bądź np. jeżeli mamy program łączący się przez internet kompletną funkcjonalność związaną z tą łącznością: namespace net_connect int make_connection(); int test_connection(); // itd... Można wtedy w kodzie poza ta przestrzenią odwoływać się do jej funkcji korzystając z prefixu takiego, jak nazwa przestrzeni oraz operatora zakresu :: Przykład: net_connect::make_connection(); Czemu ma służyć taki podział kodu? Podziałowi składowych programu na grupy, podobne do klas albo struktur. Przestrzenie nazw nie tworzą jednak żadnych obiektów, nie potrzebują tworzenia swoich instancji, żeby być używane. Po prostu aby użyć określonej funkcji piszemy tylko nazwę przestrzeni i nazwę tej funkcji. Ten podział pozwala na nadawanie tych samych nazw funkcjom należącym do różnych przestrzeni nazw, a więc chroni przed potencjalnymi konfliktami nazw w przypadku łączenia bibliotek 292 293 4

Jeżeli nie chcemy za każdym razem pisać nazwy przestrzeni możemy się do niej odwołać przez słowo kluczowe using Jeżeli w bloku kodu napiszemy using namespace nazwa_przestrzeni w obrębie tego bloku możemy odwoływać się do wszystkich funkcji należących do tej przestrzeni bez potrzeby podawania jej nazwy jako prefixu (jeżeli napiszemy tak na początku pliku dostęp do funkcji obowiązuje w obrębie całego pliku) Jeżeli chcemy odwoływać się tylko do wybranej funkcji z pewnej przestrzeni nazw możemy napisać: using nazwa_przestrzeni::nazwa_funkcji namespace A const int two = 2; void napisz() printf( A\n ); ; namespace B void napisz() printf( B\n ); ; namespace C const int two = 22; int main () using A::napisz; using namespace C; napisz(); B::napisz(); printf( %i\n,two); return 0; // co się pojawi w oknie konsoli? 294 295 A jeżeli chcemy w obrębie całego pliku korzystać ze wszystkich funkcji bibliotecznych, piszemy: using namespace std; ponieważ właśnie te funkcje są zadeklarowane w przestrzeni nazw std Przestrzenie nazw to nowy element w C++ W C też używamy plików nagłówkowych bibliotek standardowych, ale tam wszystkie nazwy w nich zadeklarowane są jednakowo dostępne bez żadnych kwalifikacji, bo nie ma tam mechanizmu przestrzeni nazw Aby zapewnić możliwość kompilowania programów w C przez kompilatory C++ przyjęto umowę: jeżeli włączymy plik w sposób tradycyjny, podając pełną nazwę pliku nagłówkowego, jest on włączany oraz automatycznie otwierana jest nowa przestrzeń nazw std. Jeżeli ten sam plik włączymy pod nową nazwą, to przestrzeń std nie jest automatycznie otwierana. Przykład: C: #include <string.h> C++: #include <cstring> 296 297 Przyjęto konwencję, że pliki nagłówkowe o tradycyjnej nazwie nazwa.h są w C++ nazywane cnazwa : assert.h cassert limits.h climits stdio.h cstdio time.h ctime ctype.h cctype stdlib.h cstdlib math.h cmath... iostream (!) W kodzie programu pisanego w C++ należy używać wersji przygotowanej dla C++. Cały kod, który został napisany poza jakąkolwiek przestrzenią nazw w rzeczywistości należy do tzw. globalnej przestrzeni nazw Jeżeli chcemy tylko zamknąć kod w swojej przestrzeni nazw, możemy napisać: namespace class Car... // składowe klasy Car // inne składowe przestrzeni nazw bez nazwy (unnamed namespace) 298 299 5

Kiedy używamy przestrzeni nazw bez nazwy, kompilator sam wymyśla dla niej unikalną nazwę Po co używać takiej przestrzeni nazw? Jeżeli deklarujemy struktury lub klasy, które będą używane tylko w lokalnym fragmencie kodu, mamy pewność, że ich nazwa nie wejdzie w kolizję z nazwą zadeklarowaną gdziekolwiek w globalnej przestrzeni nazw STRUMIENIE 300 Motywacja wprowadzenia nowej biblioteki strumieni: W języku C istnieje intepreter odpowiedzialny za analizę łańcucha formatującego podczas wykonywania programu oraz pobierający zmienną liczbę argumentów. Wady interpretera formatowanego we/wy: 1. wystarczy użyć choćby niewielką część możliwości intepretera, a cały interpreter zostaje dołączony do nowego programu 2. interpretacja wykonuje się w trakcie wykonywania programu dlatego w tym momencie zawsze następuje pewne spowolnienie jego działania 3. interpretacja wykonuje się w trakcie wykonywania programu, dlatego dopiero wtedy okazuje się, czy w łańcuchu formatującym nie było błędów 4. funkcje z rodziny printf nie są rozszerzalne operują tylko na typach wbudowanych W C++ operacje we/wy realizuje się za pomocą strumieni. Strumień może mieć kierunek do i od naszego programu strumień wyjściowy informacja płynie od naszego programu do ujścia. Ujściem może być terminal, plik, gniazdko internetowe, obszar w pamięci, nazwany potok, systemowa kolejka FIFO, modem, itp. strumień wejściowy informacja płynie do naszego programu ze źródła. Źródłem może być terminal, plik, itp. 302 303 Klasy reprezentujące strumienie wejściowe istream podstawowa klasa reprezentująca strumienie wejściowe, w niej zdefiniowany jest przeciążony operator >>. Dziedziczą z tej klasy: istringstream źródłem jest obiekt klasy string, czyli napis. Udostępniana po dołączeniu biblioteki sstream istrstream źródłem jest C-napis (czyli tablica znaków ze znakiem \0 jako ostatnim). Udostępniana po dołączeniu biblioteki strstream ifstream źródłem jest plik. Udostępniana po dołączeniu biblioteki fstream Klasy reprezentujące strumienie wyjściowe ostream podstawowa klasa reprezentująca strumienie wyjściowe, w niej zdefiniowany jest przeciążony operator <<. Dziedziczą z tej klasy: ostringstream ujściem jest obiekt klasy string, czyli napis. Udostępniana po dołączeniu biblioteki sstream ostrstream ujściem jest C-napis (czyli tablica znaków ze znakiem \0 jako ostatnim). Udostępniana po dołączeniu biblioteki strstream ofstream ujściem jest plik. Udostępniana po dołączeniu biblioteki fstream 304 305 6

Uwaga na marginesie: W C++ utworzono klasę string aby łatwiej operować na łańcuchach tekstowych: string napis1; napis1 = "text1"; string napis2( "text2" ); napis1 = napis1 + napis2; if (napis1 == napis2) cout << "takie same\n"; if (napis1 > napis2) cout << "napis1 jest wiekszy\n"; else cout << "napis2 nie jest wiekszy\n"; Uwaga na marginesie c.d.: Porównanie: C : C++: strcpy(a,b) a = b strcmp(a,b) a == b strcat(a,b) a += b strlen(a) a.size() strstr(a,b) a.find(b).. koniec uwagi na marginesie. 306 307 Klasa iostream dziedziczy zarówno z istream jak i ostream (dołączamy tylko plik nagłówkowy <iostream>) fstream dziedziczy z iostream (dołączamy plik nagłówkowy <fstream> i mamy przy okazji dostęp do całej biblioteki <iostream>) stringstream również dziedziczy z iostream (dołączamy plik nagłówkowy <sstream> i również dostajemy dostęp do całej biblioteki <iostream>) Wszystkie te klasy mają jeden interfejs, niezależnie od tego, czy masz do czynienia z plikiem, terminalem, obszarem pamięci czy obiektem string. Przykładowo klasa istream deklarowana jest jako: typedef basic_istream<char> istream; 308 309 Wszystkie te klasy są specjalizacjami szablonu Hierarchia szablonów: Wszystkie pozostałe klasy są deklarowane analogicznie. Istnieją też definicje typów dla wszystkich strumieni klas wykorzystujące zamiast char typ wchar_t, który reprezentuje znaki wielobajtowe (ten temat nie będzie rozwijany na tym wykładzie) Strumienie predefiniowane Po dołączeniu któregoś z plików nagłówkowych iostream, fstream lub sstream, mamy do dyspozycji cztery już otwarte strumienie: jeden wejściowy cin standardowy strumień wejściowy przydzielony procesowi wykonującemu program, zwykle - klawiatura trzy wyjściowe cout standardowy strumień wyjściowy przydzielony procesowi wykonującemu program, zwykle ekran terminala. Strumień jest buforowany, dlatego znaki mogą pojawiać się tam z opóźnieniem cerr standardowy strumień komunikatów o błędach przydzielony procesowi wykonującemu program, zwykle ekran terminala. Strumień nie jest buforowany clog strumień komunikatów (rzadko używany), jest buforowany 310 311 7

Operatory << i >> Aby wysłać dane do strumienia należy użyć operatora << rezultatem działania przeciążonego operatora jest odniesienie do tego samego obiektu/strumienia. Kolejność realizacji działań jest następująca (nawiasy są zbędne tutaj umieszczone tylko dla ujawnienia kolejności): ((cout << x) << y) << z; OPERATORY WE/WY: << >> Aby odczytać dane ze strumienia należy użyć operatora >> rezultatem działania również tego przeciążonego operatora jest odniesienie do tego samego obiektu/strumienia: ((cin >> x) >> y) >> z; 313 Domyślne formatowanie wartości: przy wypisywaniu: wartości całościowe (int, short, itd.) w systemie dziesiątkowym wartości znakowe jako pojedyncze znaki wartości zmiennoprzecinkowe (float, double, itd.) w systemie dziesiątkowym z precyzją 6 cyfr znaczących (np.. 200.0/3 daje 66.6667, 1.129995 zostanie najpierw zaokrąglone do 1.13000 a potem wypisane jako 1.13, ale 1.129994 jako 1.12999) wartości wskaźnikowe (adresy) jako dodatnie liczby całkowite reprezentujące adres w systemie szesnastkowym z prefiksem 0x wartości wskaźnikowe typu char* jako C-napisy wartości logiczne bool jako 0 lub 1 Domyślne formatowanie wartości: przy wczytywaniu: wiodące białe znaki są pomijane każda następna niepusta sekwencja białych znaków jest interpretowana jako koniec danych. Znaki te pozostają w buforze i będą pominięte, jako wiodące przy następnym czytaniu liczby całkowite są wczytywane w postaci dziesiętnej aż do napotkania odstępu lub znaku nie będącego cyfrą ten znak jest pozostawiany w buforze i będzie pierwszym znakiem wczytanym przy następnej operacji wczytania (napis 128-25 zostanie wczytany jako dwie liczby: 128 i -25) liczby zmiennoprzecinkowe są wczytywane w formacie liczb całkowitych bez kropki, w formacie z kropką dziesiętną i w notacji naukowej, tj. 1e-1 lub 1.20e+2, co oznacza odpowiednio: 0.1 lub 120.1) wartości logiczne bool mają postać literałów 0 i 1 314 315 Dla każdego wbudowanego typu danych istnieje przeciążona funkcja operator>> i operator<< Można też przeciążać ten operator na własne potrzeby W przypadku przeciążania należy zachować następujące wymagania: pierwszym parametrem musi być niestała referencja do strumienia (istream dla wejścia lub ostream dla wyjścia); strumień nie może być const, gdyż przetwarzanie danych strumienia powoduje zmianę jego stanu wykonane musi być wstawienie bądź pobranie danych do lub ze strumienia zwrócić trzeba referencję do strumienia, tak aby można było łączyć sekwencje operacji na strumieniu w jedną instrukcję Przykład przeciążanie operatora << : class Integer int i; public: Integer(int v): i(v) ; friend ostream& operator<< (ostream & b, const Integer & p); ; ostream& operator << (ostream & b, const Integer & p) return b << p.i; 316 317 8

Obsługa błędów strumieni Klasa ios_base zawiera cztery flagi, których można użyć do badania stanu strumienia: badbit pojawił się jakiś poważny błąd (być może związany ze sprzętem) eofbit pojawił się koniec danych wejściowych (albo fizyczny koniec pliku odpowiadającego strumieniowi, albo użytkownik zakończył strumień konsoli, np. znakiem Ctrl-Z albo Ctrl-D) failbit wystąpił błąd operacji we/wy, najprawdopodobniej wskutek nieprawidłowych danych (np. podczas odczytu liczby pojawiły się litery). Strumień nadal może być używany. Flaga ta ustawiana jest także w przypadku końca danych wejściowych goodbit wszystko w porządku, nie wystąpiły żadne błędy. Jeszcze nie pojawił się sygnał końca danych wejściowych ustawienia tych bitów sprawdzamy za pomocą odpowiednich funkcji: if (s.bad()): stan strumienia nieustalony, nie należy go więcej używać (ustawiony badbit) if (s.eof()): rozpoznano koniec pliku (ustawiony eofbit) if (s.fail()): ostatnia z wykonanych operacji nie powiodła się (ustawiony albo failbit albo badbit) if (s.good()): wszystko OK. Aby wyzerować flagi należy to zrobić ręcznie wywołując: s.clear(); 318 319 Zwykle nie interesuje nas sprawdzenie w kodzie programu poszczególnych flag ale po prostu sprawdzenie, czy wszystko jest w porządku. Wtedy zwykle w wyrażeniu logicznym wywoływany jest operator konwersji ze wskaźnika na strumień do wartości logicznej ios_base::operator void*() Np..: if (s) Metoda operator void*() wywołuje na rzecz strumienia metodę good() i zwraca jej wynik wskaźnik void*, który przyjmuje wartość NULL, kiedy wystąpił jakikolwiek błąd lub inną wartość (nie-null) kiedy jest OK. int i; while (mystream >> i) cout << i << endl; Strumienie związane z plikami - obiekty fstream, ifstream i ofstream: 320 321 Przetwarzanie plików za pomocą biblioteki iostream jest znacznie łatwiejsze i bezpieczniejsze niż korzystanie z biblioteki stdio w C: w celu otwarcia pliku wystarczy stworzyć obiekt cała prace wykona konstruktor nie trzeba jawnie zamykać pliku zajmie się tym destruktor Obiekty zdefiniowane są na podstawie szablonów tak jak poniżej: typedef basic_ifstream<char> ifstream; Tryby otwarcia pliku: ios::in plik jest otwierany jako strumień wejściowy. Tego trybu używa się do strumieni ofstream, aby uniknąć nadpisania już istniejącego pliku ios::app otwiera plik do pisania w trybie dołączania ios::ate otwiera istniejący plik (wejściowy lub wyjściowy) i przechodzi na jego koniec ios::trunc jeśli plik istnieje, usuwa jego dotychczasową zawartość ios::binary otwiera plik w trybie binarnym, trybem domyślnym jest tryb tekstowy ios::out otwiera plik do pisania. Jeśli flaga użyta zostanie do strumienia ofstream bez flagi ios::app, ios:: ate lub ios::in, zakłada się użycie ios:trunc Deklaracje w kodzie programu: ifstream in("test1.txt"); ofstream ou("test2.txt", ios::app); Flagi można łączyć operatorem bitowej alternatywy: np.: out app 322 323 9

Jeżeli tryb otwarcia jest inny niż którakolwiek z kombinacji wymienionych powyżej, otwarcie pliku kończy się niepowodzeniem 324 10