Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
116
Strumienie wejścia/wyjścia
ios 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( ) <<endl; cout<<"cout.precision = " << cout.precision( )<<endl; cout<<"cout.fill = "<< cout.fill( )<<endl; cout<<"cin.flags = "<<cin.flags()<<endl; cout<<"cout.flags = "<<cout.flags()<<endl; } cout.width = 0 cout.precision = 6 cout.fill = cin.flags = 1 cout.flags = Programowanie C++
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( ) <<endl; cout<<"cout.precision = " << cout.precision( )<<endl; cout<<"cout.fill = "<< cout.fill( )<<endl; cout<<"cin.flags = "<<cin.flags()<<endl; cout<<"cout.flags = "<<cout.flags()<<endl; } cout.width = 0 cout.precision = 6 cout.fill = cin.flags = 1 cout.flags = Programowanie C++
121
Modyfikatory klasy ios
class 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"<<endl; cout<<"Hello World"<<endl; double pi = ; cout<<" pi = "<<pi<<endl; cout.precision(16); cout.precision(20); } ############################Hello World Hello World pi = pi = pi = Programowanie C++
122
Zmiana flag formatowania
main () { int n = 234; long oldf = cout.flags(ios::hex | ios::uppercase); cout<<n<<endl; cout.flags(ios::hex | ios::showbase); cout.flags(oldf); } class ios { public: long setf (long f) ; long setf( long f, long mask); long unsetf(long mask); ... }; EA 0xea 234 main () { int n = 234; long oldf = cout.setf (ios::hex | ios::uppercase); cout<<n<<endl; cout.setf (ios::hex | ios::showbase); cout.flags (oldf); } EA 0xEA 234 Programowanie C++
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<<n<<endl; cout.setf(ios::oct | ios::basefield); } main () { char buffer[80]; cin.unsetf(ios::skipws); //czyści domyślneustawienie flagi cin>>buffer; cout<<"["<<buffer<<"]\n"; int n = 234; cout.setf(ios::hex | ios::uppercase| ios::showbase; cout<<n<<endl; cout.usetf( ios::basefield); } 0XEA 0352 Hello, World. [ ] [ Hello,] [ World.] 0XEA 234 Programowanie C++
124
Zmienne stanu klasy ios
class 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 = "<<cin.rdstate()<<endl; int n; cin>>n; } cin.rdstate = 0 22 ^Z cin.rdstate = 3 Programowanie C++
125
Operatory dla stanu ios
class 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 "<<sum<<endl; cin.clear(); while (cin>>n) cout<<" suma całkowita wynosi "<<sum<<endl; ^Z suma częściowa wynosi 150 30 50 ^Z suma całkowita wynosi 230 Programowanie C++
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ście
Litwo! Ojczyzno moja Ty jesteś jak zdrowie... ^Z main () { char c; while((c = cin.get() ) != EOF) cout<<c; cout<<endl; } Litwo! Ojczyzno moja Ty jesteś jak zdrowie... ^Z main () { char c; while(cin.get (c) ) cout<<c; cout<<endl; } main () { char buffer[80]; cin.get(buffer,8); cout<<buffer<<endl; cin.get(buffer, sizeof(buffer)); cin.get(buffer,sizeof(buffer),'.'); } Litwo! Ojczyzno moja Litwo! Ojczyzno moja Ty jesteś jak zdrowie... Ty jesteś jak zdrowie Programowanie C++
128
Nieformatowane wejście c.d.
main () { char buffer[80]; cin.getline(buffer,8); cout<<buffer<<endl; cin.getline(buffer, sizeof(buffer)); cin.getline(buffer,sizeof(buffer),'.'); } Litwo! Ojczyzno moja Litwo! Ojczyzno moja Ty jesteś jak zdrowie... Ty jesteś jak zdrowie main () { int month,year; cout<<"Podaj datę (mm/dd/rrrr): "; cin>>month; cin.ignore(); // zjada '/' cin.ignore(80,'/'); //zjada "dd/", lub "d/" lub "/" cin>>year; cout<<"Miesiąc = "<<month<<”, Rok = "<<year <<endl; } Podaj datę (mm/dd/rrrr): 1/10/2000 Miesiąc = 1, Rok = 2000 Programowanie C++
129
Nieformatowane wejście c.d.
main () { char buffer[80],c ; cout<<cin.peek( ) <<" , "<< cin.peek( ) <<" ,"; c = cin.peek( ); cout<<c<<", "; cin.get(buffer, 5); cout<<c <<" , "<< cin.peek( ) <<" , "<< cin.peek( ) <<endl; cin.putback('Z'); cin.putback('Y'); cin.get(buffer,5); cout<<buffer<<endl; } ABCDEFG 65, 65, A, E, 69, 69 YZEF main () { char buffer[] = "????????????????????"; cin.read(buffer, 8); cout<<buffer<<" read: "<<cin.gcount( ) <<endl; cin.read(buffer, 4); } ABCDEFGHIJKLMN ABCDEFGH???????????? read: 8 IJKLEFGH???????????? read: 4 Programowanie C++
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ście
main () { char c ; while(cin.get (c) ) cout.put(c); cout<<endl; cout.put('H'). put('e'). put('l'). put('l'). put('o'). put('\n'); cout.write("ABCDEFGHIJKLMNOPRSTUWXYZ",8); cout.write(" ", 4); } Litwo! Ojczyzno moja Ty jesteś jak zdrowie... ^Z Hello ABCDEF 1234 Programowanie C++
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<<endl; w rzeczywistości jest to wywołanie funkcji endl(). Popatrzmy jak wygląda realizacja tej funkcji. Klasa ostrem zawiera przeładowany operator wstawiania do strumienia w następującej postaci (folia). Parametrem jest wskaźnik do funkcji, która ma jeden parametr typu ostream. W instrukcji cout<<endl; wywoływany jest operator << z parametrem będącym funkcją endl. To jest parametr funkcyjny a więc wskaźnik p wskazuje na funkcję endl, której definicja jest następująca: (folia). Następuje więc wywołanie operatora wstawiania do strumienia ze wskaźnikiem wskazującym na cout<<endl( ) zatem instrukcja return (*p)(*this) oznacza return cout.endl(*this); a ta funkcja wstawia nową linię czyści bufor cout i zwraca cout. W ten sposób pracują wszystkie manipulatory. Programowanie C++
133
Manipulatory strumieni
Najbardziej popularne manipulatory strumieni: Programowanie C++
134
Definiowanie manipulatorów
Manipulatory 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<<beep; Programowanie C++
135
Zastosowanie manipulatorów
ostream & tem(ostream& str){ str<<setprecision(1)<<setiosflags(ios::showpos) <<setiosflags(ios::fixed) <<resetiosflags(ios::scientific); return str; } ostream & wys(ostream& str){ str<<resetiosflags(ios::showpos); ostream & ges(ostream& str){ str<<resetiosflags(ios:: fixed) <<setiosflags(ios:: scientific) <<setprecision(4); main(){ float t=20.47, g= ; int w=3000; cout<<" temperatura = "<<tem<<t <<" wysokość = "<<wys<<w <<" gestość = "<<ges<<g <<endl; } Grębosz podaje taki przykład – załóżmy, że w programie mamy często wypisywać dane takich typów: temperaturę – z dokładnością do jednego miejsca po kropce dziesiętnej i z zaznaczeniem znaku plus lub minus, wysokość – w liczbach całkowitych, (bez znaku plus bo ujemnej być nie może) gęstość – w tzw. notacji naukowej Jeżeli mamy to robić często to zamiast za każdym razem wprowadzać odpowiedni format lepiej zdefiniować własne manipulatory do wyświetlania każdego typu danej. Wtedy w programie głównym wypisywanie tych wartości jest banalne. temperatura = wysokość = 3000 gęstość = 9.273e-02 Programowanie C++
136
Operacje wejścia/wyjścia na plikach
Trzy 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 plikach
Zdefiniowanie 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 <fstream.h> main(){ ostream plik_wyj; plik_wyj.open("pliczek.txt"); plik_wyj<<"Jakis tekst"; plik_wyj.close( ); } Programowanie C++
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 <iostream.h> #include <fstream.h> #include ”point.h" main() { clrscr(); char* nazwa="pliktest.000"; fstream plik(nazwa, ios::out|ios::binary); Point xx; if (plik) { plik.write((char *)&xx, sizeof(xx)); xx.Set(2,2); plik.write((char *)&xx, sizeof(xx)); } plik.close(); plik.open(nazwa, ios::in|ios::out|ios::nocreate); if (plik) while(!plik.eof()) {plik.read((char*)&xx,sizeof(xx)); cout<<xx.X()<<" "<<xx.Y()<<endl;} cout<<" poz. wsk. do czytania "<<plik.tellg()/sizeof(xx)<<endl; plik.seekp(plik.tellg()); xx.SetPoint(3,3); plik.write((char *)&xx, sizeof(xx)); plik.seekg(plik.tellp()-sizeof(xx)); plik.read((char*)&xx,sizeof(xx)); cout<<xx.X()<<" "<<xx.Y()<<endl; } Pliki binarne służą do przechowywania rekordów o określonej liczbie bajtów Funkcje składowe read i write służą do zapisu i odczytu określonej liczby bajtów Obie funkcje wymagają dwóch parametrów: pierwszego typu char* , drugiego typu int określającego liczbę lub odczytywanych bajtów. 0 0 2 2 poz.wsk.do czytania 2 3 3 Programowanie C++
140
Wskaźnik pozycji w pliku
Każ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 <fstream.h> #include <stdlib.h> #include <ctype.h> main( int arg, char** argv) { fstream iofile(argv[1], ios::in|ios::out); if (!iofile) { cerr<<"Error: nie można otworzyc pliku"<<endl; exit(1); } char c; while( c=iofile.get())!=EOF) if (islower(c) { iofile.seekp(-1,ios:::cur); iofile.put(toupper(c)); iofile.close( ); Niniejszy program przedstawia przykład wykorzystania dostępnych w C++ strumieni, które równocześnie umożliwiają odczyt i zapis do pliku. Plik nagłówkowy stdlib.h jest potrzebny by skorzystać z funkcji exit, natomiast ctype aby wykorzystać funkcje islower i toupper. Najpierw definiujemy strumień, który będzie powiązany z plikiem o nazwie przekazywanej przez listę argumentów programu. Załóżmy, że program nazywa się modyfikuj. W pierwszej instrukcji widzimy wykorzystany operator !( ), który był zdefiniowany w klasie ios, jeśli otwarcie pliku nie powiodło się, to program kończy działanie wcześniej przesyłając komunikat do standardowego strumienia błędów cerr. Jeśli natomiast plik został otwarty, to wykonujemy w pętli tak długo aż napotkamy koniec pliku pobieranie znaku, sprawdzanie czy jest to mała litera i jeśli tak to zamiana na dużą. Ta zamiana odbywa się poprzez wykorzystanie funkcji seekp – ustawienie wskaźnika wstawiania o jeden znak wcześniej od aktualnej pozycji. Inw.txt Litwo! Ojczyzno moja Ty jesteś jak zdrowie... modyfikuj Inw.txt LITWO! OJCZYZNO MOJA TY JESTEŚ JAK ZDROWIE... Programowanie C++
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 <class T> void swap(T& x, T& y) { T temp = x; x = y; y = temp; } main ( ) { int m = 22, n = 33; cout<<" m = "<<m<<" n = "<<n<<endl; swap(m,n); Date d1,d2(1999,9,9); cout<<"d1 = "<<d1<<" d2 = "<<d2<<endl; swap(d1,d2); } m = 22 n = 33 m = 33 n = 22 d1 = d2 = d1 = d2 = Programowanie C++
143
Szablony klas Szablony klas działają jak szablony funkcji generując klasy. template <class T,...> class X {...}; Funkcje składowe szablonu klasy są szablonami funkcji o takim samym nagłówku jak szablon klasy. template <class T, int n> class X{}; main( ) { X <float, 22> x1; //O.K. const int n = 44; X<char, n> x2; //O.K. int m = 66; X <short, m> x3; //ERROR } template <class T> class X { T square (T t) { return t*t; } }; template <class T> T square (T t) { return t*t; } Programowanie C++
144
Szablon klasy stos template <class T> class Stack { private:
int size; int top; T* data; public: Stack( int s = 100): size(s); top = -1; { data = new T[size]; } ~Stack( ) {delete [ ] data}; void push (const T& x) {data[++top] = x; } T pop() { return data[top--];} int isEmpty( ) const {return top = = -1;} int isFull( ) const { return top = = size - 1; } }; main ( ) { Stack<int> stosint(5); Stack<Data> stosdata(10); Data x, y(2000,1,17); stosint.push(13); stosint.push(2); stosdata.push(x); stosdata.push(y); cout<<stosint.pop( )<<„ „<<stosint.pop( )<<endl; stosdata.pop( ).Display( ); } 2 13 Programowanie C++
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 <class T> class Vector{ protected: T* data; unsigned size; void copy(const Vector<T>&); public: Vector (unsigned n = 10) : size(n), data( new T[size]) { } Vector ( const Vector<T>& other) : size(other.size), data( new T[size]) { copy(other); } ~Vector {delete [ ] data;} Vector<T>& operator = (const Vector<T>&); T& operator [ ] (unsigned i) const {return data[ i ];} usigned Size( ) const {return size;} }; Programowanie C++
146
Definicja funkcji składowych
template <classT> Vector<T>& Vector<T>::operator = (const Vector<T>& other) { size = other.size; data = new T[size]; copy( other); return this*; } template <class T> void Vector<T>::copy( const Vector<T>& other) unsigned min_size = (size < other.size ? size : other.size); for (int i =0; i<min_size; i++) data[i] = other.data[i]; Vector<short> v; v[5] = 34; Vector<short> w = v, x(3); cout<<w.size( ); Programowanie C++
147
Dziedziczenie szablonów
Dziedziczenie szablonów klas działa tak samo jak dziedziczenie zwykłych klas. template <class T> class Array : public Vector<T>{ protected: int i0; public: Array(int i, int j): i0(i), Vector<T>(j-i+1) { } Array(const Array<T>& other): i0(other.i0), Vector<T>(other) { } T& operator [ ] (int i) const { return Vector<T>::operator [ ] (i-i0); } int firstSubscript( ) const { return i0;} int lastSubscript ( ) const { return i0+size - j; } }; #include<iostream.h> #include "Array.h" main ( ) { Array<float> x(1,3); x[1] = 2.22; x[2] = 3.33; x[3] = 4.44; cout<"x.Size( ) = "<<x.Size( )<<endl; cout<<x.fistSubscript( )<<".."<<lastSubscript( )<<endl; for (int i = 1, i<=3, i++) cout<<"x["<<i<<"] = "<<x[i]<<endl; } x.Size( ) = 3 1..3 x[1] = 2.22 x[2] = 3.33 x[3] = 4.44 Programowanie C++
148
Szablony jako parametry szablonów
Ponieważ szablony klas pracują jak zwykłe klasy to można je przekazywać jako parametry innych szablonów Stack <Vector<int> > a; template <class T > class Matrix{ protected: Vector<Vector<T>*> row; public: Matrix( unsigned r =1, unsigned c=1) : row { for (int i=0; i<r; i++) row[i] = new Vector<T> (c); } ~Matrix () { for (int i = 0; i<row.size(); i++) delete row[i]; } Vector<T>& operator [ ](unsigned i) const { return *row[i]; } unsigned rows() { return row.size( ) } unsigned columns () { return row[0] -> size( ); } }; main ( ) { Matrix<float> a(2,3); a[0][0] = 0.0; a[0][1] = 0.1; a[0][2] = 0.2; a[1][0] = 1.0; a[1][1] = 1.1; a[1][2] = 1.2; cout<<"Macierz ma "<<a.rows( )<<" wiersze i " <<a.columns( ) <<" kolumny.\n"; for (int i=0; i<2; i++) { for (int j=0; j<3; j++) cout<<a[i][j]<<" "; cout<<endl; } Macierz ma 2 wiersze i 3 kolumny Programowanie C++
149
Szablon klasy dla list jednokierunkowych
Listy - 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 <class T> class ListNode { friend class List<T>; protected: T data; ListNode* next; public: ListNode(T& t, ListNode<T>* p): data(t), next(p) { } }; 12 p next int ListNode< int > template <class T> class List { protected: ListNode<T>* first; ListNode<T>* newNode( T& t, ListNode<T>* p) { ListNode<T>* q = new ListNode<T>(t,p); return q; } ... Programowanie C++
150
Funkcje składowe szablonu listy
Konstruktor 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 <class T> List <T>::~List ( ) { ListNode<T>* temp; for (ListNode<T>* p = first; p; ) temp = p; p = p->next; delete temp; } Funkcja insert tworzy nowy węzeł i wstawia go na początek listy: template <class T> void List <T>::insert (T t) { ListNode<T>* p = newNode(t,first); first = p; } Programowanie C++
151
Listy c.d. 3 -> 2 -> 1 -> 0 -> * Usunięto 2
#include <iostream.h> #include "List.h" main( ) { List<int> liczby; liczby.insert(0); liczby.insert(1); liczby.insert(2); liczby.insert(3); liczby.print( ); int x; liczby.remove(x); cout<<"Usunięto "<<x<<endl; } template <class T> int List <T>::remove ( T& t) { if (isEmpty()) return 0; t = first->data; ListNode<T>* p = first; first = first->next; delete p; return 1; } void List <T>::print ( ) for (ListNode<T>* p = first; p; p = p ->next) cout<<p -> data << "-> ”; cout<<”* \n”; 3 -> 2 -> 1 -> 0 -> * Usunięto 2 2 -> 1 -> 0 -> * data 2 next data 1 next data next Programowanie C++
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 <class T> class Iterator { public: virtual int reset( ) = 0; virtual T operator ( )( ) = 0; virtual void operator = (T t) = 0; virtual int operator ! ( ) = 0; virtual int operator ++( ) =0; }; Programowanie C++
153
Szablon iteratora dla szablonu klasy List
// plik ListIter.h #include ”List.h” #include ”Iterator.h” template <class T> class ListIter: public Iterator<T> { protected: ListNode<T>* current; ListNode<T>* previous; List<T>& list; public: ListIter(List<T>& l):list(l) {reset( );} virtual void reset( ) {previous = NULL; current = list.first;} virtual T operator ( ) ( ) {return current->data;} virtual void operator = (T t) {current->data=t;} virtual int operator ! ( ) ; virtual int operator ++( ) ; void insert (T t); void preInsert(T t); void remove( ); }; template <class T> int ListIter<T>::operator ! ( ) { if (current==NULL) if (previous==NULL) current=list.first; else current=previous->next; return (current!=NULL); } if (!it)... template <class T> int ListIter<T>::operator ++ ( ) { if (current==NULL) if (previous==NULL) current=list.first; else current=previous->next; else { previous=current; current=current->next; } return (current!=NULL); } for (it.reset(); !it; it++)... Programowanie C++
154
#include "List.h" #include "ListIter.h" #include "Date.h" main( ) { List<Date> Daty; ListIter<Date> it(Daty); Date today; it.insert(today); today.Forth(); it++; today.Back(); Daty.print(); Date my(1994,4,19); it.reset(); it=my; it.remove(); for (it.reset(); !it; it++) {Date temp=it(); temp.Forth(); it = temp; } } template <class T> void ListIter<T>::insert(T t) { ListNode<T>* p=list.newNode(t,0); if (list.isEmpty( ) ) list.first=p; else { p->next= current->next; current->next=p; } } void ListIter<T>:remove( ) { if (current==list.first) list.first = current->next; else previous->next= current->next; delete current; current = 0; > > >* > >* Programowanie C++
155
Przyjaciel Listy Lista może posiadać więcej niż jeden iterator:
// List.h template <class T> class List { friend class ListIter<T>; //.... }; class ListNode { friend class List<T>; Lista może posiadać więcej niż jeden iterator: Iteratory są od siebie niezależne. List<float> list; ListIter<float> it1(list), it2(list),it3(list); it1.insert(11.01); it1++; it1.insert(22.02); it1.insert(33.03); for (it2.reset(); !it2; it2++) it2=10*it2; it3=it1; Programowanie C++
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.