116 Strumienie wejścia/wyjściaios istream ostream ifstream iostream ofstream istrstream istringstrem ostrstream ostringstream fstream strstream streambuf filebuf stdiobuf strstrambuf stringmbuf Programowanie C++
117 Klasa ios class ios { public:typedef unsigned long fmtflags; // 32-bitowy łańcuch bitowy typedef unsigned char iostate; // 8-bitowy łańcuch bitowy protected: streambuf* _strbuf; // wskaźnik do bufora ostream* _tie; // wskaźnik do ostream dołączonego do istream int _width; // szerokość pola wyjściowego int _prec; // dokładność dla float char _fill; // znak wypełnienia pustych pól fmtflags _flags; // flagi formatowania iostate _state; // aktualny stan io ios(streambuf* _strbuf = 0, ostream* _tie = 0); X ~ios(); ... }; _width _prec _fill _flags _state _strbuf _tie 15 9 ’#’ 03240 03 Programowanie C++
118 Flagi formatowania class ios { public:enum { skipws = , // pomiń białe znaki (domyślne) left = , // wyrównanie lewostronne right = , // wyrównanie prawostronne (domyślne) internal = , // wyrównanie lewo i prawostronne dec = , // konwersja dziesiątkowa oct = , // konwersja ósemkowa hex = , // konwersja szesnastkowa showbase = , // pokaż podstawę konwersji showpoint = , // pokaż kropkę dziesiętną uppercase = , // duże litery w zapisie liczb showpos = , // użyj + z liczbami dodatnimi scientific = , // użyj notacji naukowej dla float i double fixed = , // użyj notacji z kropką dziesiętną unitbuf = , // buforowanie... stdio = }; // ...współpraca z stdio ... }; Programowanie C++
119 Selektory klasy ios class ios { public:streambuf* rdbuf () const { return _strbuf ; } ostream* tie () const { return _tie; } int width () const { return _width; } int precision () const { return _prec; } char fill () const { return _fill; } long flags () const { return _flags; } int rdstate () const { return _state; } ... }; Ponieważ wszystkie składowe klasy ios są protected więc aby do nich dotrzeć potrzebujemy publicznych funkcji składowych. Standardowo w klasie mamy do dyspozycji funkcje dostępu czyli selektory zwracające stan poszczególnych danych składowych . Przykładowy program pokazuje jakie są domyślne ustawienia poszczególnych składowych: Szerokość pola jest przyjęta na zero, precyzja na sześć co oznacza, że właśnie taka ilość miejsc po przecinku dla zmiennych rzeczywistych jest wyświetlana. Znakiem wypełnienia pustych miejsc jest spacja natomiast wartość flagi oznacza dla strumienia cin, że tylko pierwsza flaga jest ustawiona natomiast dla strumienia cout ustawiona jest flaga pierwsza i oct. Liczba 2 na początku tej wartości jest nieważna. main () { cout<<"cout.width = "<< cout.width( ) <
120 Selektory klasy ios class ios { public:streambuf* rdbuf () const { return _strbuf ; } ostream* tie () const { return _tie; } int width () const { return _width; } int precision () const { return _prec; } char fill () const { return _fill; } long flags () const { return _flags; } int rdstate () const { return _state; } ... }; main () { cout<<"cout.width = "<< cout.width( ) <
121 Modyfikatory klasy iosclass ios { public: int width (int w) { int t = _width; _width = w; return t; } int precision (int p) {int t = _precision; _precision = t; return t; } char fill (char c) { char t = _fill; _fill = c; return t; } long flags (long f) { long t = _flags; _flags = f; return t; } ... }; Selektory służyły tylko do sprawdzenia stanu poszczególnych ustawień. Oczywiście musimy mieć możliwość zmiany tych ustawień. Do dyspozycji mamy trzy różne sposoby: albo używając funkcji składowych, których nazwy przypominają to, co robią; albo używając bardzo elementarnych funkcji klasy ios ustawiających całe słowo formatów, albo wykorzystując manipulatory. Najpierw o tym pierwszym sposobie. Każda z czterech danych składowych _width, _precision, _fill i _flags może być zmieniona poprzez odpowiedni modyfikator. Funkcja width ustala minimalną liczbę miejsc przeznaczonych na wypisanie danej liczby. Czyli jeśli liczba cyfr znaczących jest większa niż ustalona szerokość to żadnej z tych cyfr znaczących nie zgubimy. Natomiast nie ma możliwości ustalenia maksymalnej liczby znaków na których ma być wypisana liczba – gdyż mogłoby to prowadzić do obcięcia cyfr znaczących. Ten parametr wykorzystywany jest do wypisywania danych w postaci tabel. Funkcja ta ustala szerokość tylko dla następnej operacji we/wy. Jeśli chodzi o strumień wejścia, to dla wczytywanych liczb ten składnik nie ma znaczenia. Natomiast ma znaczenie dla łańcuchów. Pamiętamy, że wczytując tablicę znaków, nie jest sprawdzane przekroczenie zakresu. Pierwsze znaki są wstawiane do tablicy a następne do kolejnych komórek pamięci co może spowodować zmianę istniejących tam informacji. Czyli jest to sposób na ustawienie maksymalnej wczytywanej liczby znaków: np. Char napis [50]; cin.width(sizeof(napis)); cin>>napis; Przykład pokazuje również, że dla 32-bitowych maszyn 16 jest maksymalną liczbą miejsc po przecinku dla typu double i zwiększenie precyzji do 20 spowoduje wyświetlenie śmieci na pozostałych czterech miejscach. Jak widać trzy pierwsze funkcje zwracają wartość, która obowiązywała dotychczas. Funkcja fill pozwala zmienić znak wypełnienia nieznaczących miejsc i w przeciwieństwie do poprzedniej funkcji daje trwały efekt. Podobnie trwały efekt ma ustawienie funkcji precision, która pozwala określić dokładność z jaką mają być wyświetlane na ekranie liczby zmiennopozycyjne. main () { cout.fill('#'); cout.width(40); cout<<"Hello World"<
122 Zmiana flag formatowaniamain () { int n = 234; long oldf = cout.flags(ios::hex | ios::uppercase); cout<
123 Maski flag formatu class ios { public:const long basefield = dec | oct | hex; const long adjustfield = left | rigth | internal; const long floatfield = scientific | fixed; ... }; main () { int n = 234; cout.setf(ios::hex | ios::uppercase| ios::showbase); cout<
124 Zmienne stanu klasy iosclass ios { public: enum { goodbit = 0, // wszystko ok eofbit = 01, // koniec pliku failbit = 02, // ostatnia operacja zakończona niepomyślnie badbit = 04 }; // niewłaściwa operacja ... }; Dostępne selektory: good(), eof(), fail(), bad(), rdstate() main () { cout<<" cin.rdstate = "<
125 Operatory dla stanu iosclass ios { public: operator void* () const; //operator konwersji int operator! () const; void clear (int ); // modyfikator - ustawia nowe słowo stanu strumienia ... }; main () { int n, sum=0; cin>>n; while (cin) { // pętla będzie wykonywana tak długo dopóki _state ==0 sum+=n; } cout<<" suma częściowa wynosi "<
126 Klasa istream Nieformatowane funkcje wejścia: int get ( );class istream : virtual public ios { // ...} zdefiniowanie ios jako wirtualnej klasy bazowej ułatwia wielokrotne dziedziczenie, które posiada klasa iostream Strumień cin oraz operator >> klasy istream obsługują formatowane operacje wejścia. Nieformatowane funkcje wejścia: int get ( ); istream& get ( char& c ); istream& get ( char* buffer, int n, char delim = ’\n’ ); istream& getline ( char* buffer, int n, char delim = ’\n’); istream& ignore ( int n = 1, int delim = EOF); int peek ( ); istream& putback (char c); istream& read ( char* buffer, int n); istream& read ( unsigned char* buffer, int n); int gcount ( ); Programowanie C++
127 Nieformatowane wejścieLitwo! Ojczyzno moja Ty jesteś jak zdrowie... ^Z main () { char c; while((c = cin.get() ) != EOF) cout<
128 Nieformatowane wejście c.d.main () { char buffer[80]; cin.getline(buffer,8); cout<
130 Klasa ostream Nieformatowane funkcje wyjścia: int put ( char c );class ostream : virtual public ios { // ...} Strumienie: cout // standardowe urządzenie wyjścia cerr // standardowe urządzenie wyjścia dla komunikatów błędów (niebuforowany) clog // jak wyżej, strumień buforowany wraz z operatorem << obsługują formatowane wyjście. Nieformatowane funkcje wyjścia: int put ( char c ); ostream& put ( char c ); ostream& write ( const char* buffer, int n); ostream& write ( const unsigned char* buffer, int n ); Programowanie C++
131 Nieformatowane wyjściemain () { char c ; while(cin.get (c) ) cout.put(c); cout<
132 Manipulatory Manipulatory – specjalne funkcje, które wstawiane do strumieni wyglądają jak obiekty i dokonują zmiany sposobu formatowania Operator wstawiania do strumienia: ostream& operator <<(ostream& (*p)(ostream&)) { return (*p)(*this); } Manipulator endl: ostream& endl(ostream& ostr) { ostr.put(‘\n’); ostr.flush(); } Manipulator strumienia to specjalna funkcja składowa klasy, która jeśli jest używana razem z operatorem wstawiania bądź pobierania ze strumienia wygląda jak obiekt. Do tej pory najczęściej korzystaliśmy z manipulatora endl w następujący sposób: cout<
133 Manipulatory strumieniNajbardziej popularne manipulatory strumieni: Programowanie C++
134 Definiowanie manipulatorówManipulatory strumieni definiowane są zgodnie z następującymi prototypami: - manipulatory bezparametrowe ios& f( ios& ostr); ostream& f( ostream& ostr ); istream& f ( istream& istr); - manipulatory z parametrem ios& f( ios& ostr , int n ); ostream& f( ostream& ostr , int n ); istream& f ( istream& istr , int n ); ostream& beep(ostream& ostr) { return ostr<<„\a”; } main ( ) cout<
135 Zastosowanie manipulatorówostream & tem(ostream& str){ str<
136 Operacje wejścia/wyjścia na plikachTrzy rodzaje strumieni zdefiniowane dla operacji na plikach: ofstream - zapis do plików, ifstream - odczyt z plików, fstream - odczyt i zapis do tego samego pliku Otwarcia plików wejściowych i wyjściowych można dokonać za pomocą konstruktorów lub funkcji open (mają takie same parametry): void open ( char* name, int mode = ***, int prot = filebuf::openprot ); ifstream infile (" testfile.txt"); // deklaracja zmiennej i otwarcie pliku ifstream infile; // deklaracja zmiennej infile.open("testfile.txt"); if (infile.fail()) { // otwarcie pliku nie powiodło się; //wyświetl komunikat błędu i wróć do programu } if (!infile) {// otwarcie pliku nie powiodło się;} ifstream* infile; //deklaracja wskażnika infile = new ifstream("testfile.txt"); // utworzenie strumienia i otwarcie pliku ifstream infile; // deklaracja zmiennej infile.open("testfile.txt"); // otwarcie pliku Programowanie C++
137 Operacje wejścia/wyjścia na plikachZdefiniowanie strumienia ( obiekt klasy ifstream, ofstrem lub iofstream ) Określenie i otwarcie konkretnego pliku Wykonanie operacji we/wy Likwidacja strumienia Jeśli chcemy zapisywać cos do plików lub z nich czytać mamy do dyspozycji klasy ifstream, ofstream i fstream będące pochodnymi klas, które omawialiśmy do tej pory: istream, ostream i iostream. Aby skorzystać z tego fragmentu biblioteki należy do programu dołączyć plik nagłówkowy fstream.h. Skoro te klasy dziedziczą od klas istream i ostream to oznacza to, że mamy do dyspozycji wszystkie cechy i zachowania, o których mówiliśmy do tej pory. Podstawowa różnica polega na tym, że teraz strumienie nie są już predefiniowane – to jasne, bo to my sami musimy zdecydować do jakich plików będziemy pisać lub z jakich czytać. Jest to bardzo proste jak pokazuje przykładowy program. Najpierw definiujemy obiekt klasy ofstream, który nazywa się plik_wyj, następnie poprzez wywołanie funkcji open mamy powiązany strumień z konkretnym plikiem. Coś wstawiamy do strumienia czyli piszemy do pliku, a po zakończeniu działań zamykamy plik. #include
138 Otwarcie plików Otwarcia plików wejściowych i wyjściowych można dokonać za pomocą konstruktorów lub funkcji open (mają takie same parametry): void open ( char* name, int mode = ***, int prot = filebuf::openprot ); Tryby otwarcia plików: Otwarcia strumienia można dokonać albo za pomocą funkcji open albo za pomocą konstruktorów – w obu przypadkach mamy takie same argumenty. Mamy tutaj postać funkcji open. Pierwszym parametrem jest nazwa pliku podana jako łańcuch znaków. Drugim parametrem jest tryb otwarcia pliku. Oczywiście w tym miejscu nie ma gwiazdek, natomiast wartosć domyślna tego parametru zależy od klasy, z której wywołujemy funkcję open. W klasie ifstream tym trybem jest ios::in, w ofstream jest ios::out, natomiast w klasie fstream nie ma domyślnej wartości. Trybów otwarcia plików jest kilka i nie wszystkie się wykluczają co oznacza, że można ustawić kilka równocześnie. Tryby te są zdefiniowane jako publiczny typ wyliczeniowy w klasie ios. Programowanie C++
139 Pliki binarne Pliki binarne służą do przechowywania#include
140 Wskaźnik pozycji w plikuKażdy plik posiada swój wskaźnik (albo do czytania, albo do pisania, albo dwa niezależne wskaźniki jeden do czytania a drugi do pisania). Wskaźniki te są typu streampos streampos tellg( ); // funkcja klasy istream pokazuje jakie jest położenie wskaźnika do czytania streampos tellp( ); // funkcja klasy ostream pokazuje położenie wskaźnika pisania enum seek_dir // typ wyliczeniowy zdefiniowany w klasie ios określający punkt odniesienia { beg, // początek cur, // aktualna pozycja end }; // koniec istream& seekg (streampos, seek_dir = ios::beg); ostream& seekp(streampos, seek_dir = ios::beg); Plik w C++ może być otwarty jednocześnie do zapisu i oczytu. Korzystamy wówczas z klasy fstream: fstream strum(„plik.txt”, ios::in | ios::out) Programowanie C++
141 Modyfikacja pliku tekstowego#include
142 Szablony funkcji Szablon - abstrakcyjny przepis na tworzenie konkretnego kodu. T jest parametrem szablonu Szablon musi być zdefiniowany w zakresie globalnym. void swap( int& n, int& m) { int temp = n; n = m; m = temp; } void swap(Date& d1, Date& d2) Date temp = d1; d1 = d2; d2 = temp; template
144 Szablon klasy stos template
145 Pojemniki Pojemnik to obiekt, który zawiera inne obiekty (np.tablica, stos). Klasa pojemnikowa (klasa - pojemnik) to klasa, której obiekty są pojemnikami. Pojemnik zwany jest homogenicznym jeśli wszystkie jego obiekty są tego samego typu lub heterogenicznym w przeciwnym przypadku. template
146 Definicja funkcji składowychtemplate
147 Dziedziczenie szablonówDziedziczenie szablonów klas działa tak samo jak dziedziczenie zwykłych klas. template
148 Szablony jako parametry szablonówPonieważ szablony klas pracują jak zwykłe klasy to można je przekazywać jako parametry innych szablonów Stack
149 Szablon klasy dla list jednokierunkowychListy - struktury pozwalające na dynamiczną alokację pamięci, tworzone jako połączony ciąg węzłów, z których każdy zawiera dane składowe oraz wskaźnik do następnego węzła. data 12 t template
150 Funkcje składowe szablonu listyKonstruktor domyślny ustawia wskaźnik first na 0 Destruktor będzie likwidował całą listę: ... public : List ( ) : first(0) { } ~List ( ); void insert (T t); int remove (T& t); int isEmpty ( ) { return first == 0;} void print ( ); }; template
151 Listy c.d. 3 -> 2 -> 1 -> 0 -> * Usunięto 2#include data << "-> ”;
152 Iteratory Iterator - obiekt mający zdolność poruszania się po elementach pojemników; działający jak wskaźnik pokazujący w danym momencie jeden element należący do pojemnika. Podstawowe operacje iteratora: inicjalizacja iteratora na początkowej pozycji pojemnika, pobranie wartości danych znajdujących się we wskazywanej pozycji , zmiana wartości danych na określonej pozycji, określenie czy we wskazywanej przez iterator pozycji znajduje się jakaś wartość, przesunięcie do następnej pozycji pojemnika. // Iterator.h template
153 Szablon iteratora dla szablonu klasy List// plik ListIter.h #include ”List.h” #include ”Iterator.h” template
154 #include "List.h" #include "ListIter.h" #include "Date.h" main( ) { List
155 Przyjaciel Listy Lista może posiadać więcej niż jeden iterator:// List.h template