Operatory na rzecz typu TString

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

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

dr inż. Jarosław Forenc

Podstawy Programowania

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

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

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

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

Język C++ wykład VIII

EGZAMIN 2 (14 WRZEŚNIA 2015) JĘZYK C++

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

PARADYGMATY PROGRAMOWANIA Wykład 3

Projektowanie klas c.d. Projektowanie klas przykład

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

TEMAT : KLASY DZIEDZICZENIE

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

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

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

STL: Lekcja 1&2. Filozofia STL

Programowanie obiektowe w języku C++ dr inż. Jarosław Forenc

Zaawansowane programowanie w języku C++ Biblioteka standardowa

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

Wstęp do Programowania 2

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Wykład 8: klasy cz. 4

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

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

Programowanie obiektowe - Przykładowe zadania egzaminacyjne (2005/2006)

Wstęp do Programowania 2

PARADYGMATY PROGRAMOWANIA Wykład 2

Szablony klas, zastosowanie szablonów w programach

Programowanie i struktury danych

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

Jzyk C++ cz 3. Jarosław Gramacki Instytut Informatyki i Elektroniki ( $)*)+' *, - ( ' )*'.' '',*/ *, ','*0) 1 / ) %*+ 2'' 2" ( $%%) )'20 )*0) 1 / )

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

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

Algorytmy i Struktury Danych. Anna Paszyńska

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Konstruktor kopiujacy

Programowanie obiektowe i C++ dla matematyków

Paradygmaty programowania. Paradygmaty programowania

Pliki wykład 2. Dorota Pylak

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

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

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

Programowanie w C++ Wykład 6. Katarzyna Grzelak. 1 kwietnia K.Grzelak (Wykład 6) Programowanie w C++ 1 / 43

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

#include <iostream> #include <string> using namespace std; auto main() -> int { string s1; // pusty string. Klasa std::string

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

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

PARADYGMATY PROGRAMOWANIA Wykład 4

Wyliczanie wyrażenia obiekty tymczasowe

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

Programowanie w C++ Wykład 14. Katarzyna Grzelak. 3 czerwca K.Grzelak (Wykład 14) Programowanie w C++ 1 / 27

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

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

Programowanie w języku C++

Programowanie w C++ Wykład 6. Katarzyna Grzelak. kwiecień K.Grzelak (Wykład 6) Programowanie w C++ 1 / 40

Wyjątki (exceptions)

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

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Programowanie w C++ Wykład 7. Katarzyna Grzelak. 23 kwietnia K.Grzelak (Wykład 7) Programowanie w C++ 1 / 40

Programowanie obiektowe w C++ Wykład 12

Programowanie w językach

Plik klasy. h deklaracje klas

Programowanie Obiektowo Zorientowane w języku C++ Klasy, pola, metody

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

obiekty funkcyjne - funktory

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

Programowanie Obiektowe i C++

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

Język C++ wykład VII. uzupełnienie notatek: dr Jerzy Białkowski. Programowanie C/C++ Język C++ wykład VII. dr Jarosław Mederski. Spis.

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Programowanie obiektowe. Dr hab. Inż. Marta Gładysiewicz-Kudrawiec Pokój 229 A1 Operatory new delete pliki-odczyt

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Jak Windows zarządza pamięcią?

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

Aby uzyskać zaliczenie w pierwszym terminie (do 30 stycznia 2018) rozliczyć trzeba co najmniej 8 projektów, po 4 z każdej z części: C++ oraz Python.

Programowanie w C++ z użyciem kontenerów - parę przykładów programów Opracowanie: dr hab. Mirosław R. Dudek, prof. UZ

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

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

Część 4 życie programu

Programowanie obiektowe i C++ dla matematyków

Dzisiejszy wykład. Klasa string. wersja prosta wersja ze zliczaniem odwołań. Wyjątki Specyfikator volatile Semafory

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

Programowanie Obiektowew języku C++ Zadania L4

C-struktury wykład. Dorota Pylak

Język C++ zajęcia nr 2

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

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

Opus Magnum C++11 : programowanie w języku C++. T. 2 / Jerzy Grębosz. Gliwice, cop Spis treści

Wstęp do wiadomości teoretycznych (nie, nie jest to masło maślane ani wstęp, wstępów proszę cierpliwie czytać)

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

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

Programowanie obiektowe, wykład nr 7. Przegląd typów strukturalnych - klasy i obiekty - c.d.

referencje Wykład 2. Programowanie (język C++) Referencje (1) int Num = 50; zdefiniowano zmienną Num (typu int) nadając jej wartość początkową 50.

Języki programowania obiektowego Nieobiektowe elementy języka C++

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

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

Transkrypt:

Operatory na rzecz typu TString Dopiszmy w definicji klasy operator[], dzięki któremu potraktujemy obiekt jak tablicę class TString { public: char& operator[]( size_t n ); const char& operator[]( size_t n ) const; ; // w pliku tstring.cpp umieszczamy definicje char& TString::operator[]( size_t n ) { // włączcie nagłówek <stdexcept> // na przykład sprawdźmy czy podane n jest poprawne if (!ptr ) throw invalid_argument("pusty obiekt"); if ( n >= 0 && n < len ) return ptr[ n ]; // co zrobić gdy n jest niepoprawne? // czy można np. napisać return '0';? // ponieważ zwracamy przez referencję więc // jedynym rozsądnym wyjściem jest zgłosić wyjątek throw out_of_range("in TString::operator[] argument out of scope"); // w programie main np. : s1 = "zebym nie byl pusty"; cout << s1[3] << endl; // spróbuj też ze złym indeksem const char& TString::operator[]( size_t n ) const { /* identycznie jak powyżej */

TString jako kontener Każdy kontener ma kilka użytecznych metod są one krótkie w treści, można je definiować bezpośrednio w klasie: class TString { public: char* begin() { return ptr; char* end() { return ptr+len; size_t length() const { return len; // hmm void clear() { delete [] ptr; ptr = nullptr; len = 0; bool empty() const { return len? false : true; char& front() { return *ptr; const char& front() const { return *ptr; char& back() { return *(ptr+len-1); const char& back() const { return *(ptr+len-1); ; // tak naprawdę powinniśmy zwrócić // iterator, ale na razie stworzymy // uproszczone wersje Teraz np. (w programie main) można użyć pętlę range-based loop for ( auto& n : s1 ) { ++n; endl ( cout ); // zmieniamy literki for ( const auto& n : s1 ) cout << n << " "; cout << endl; // odczytujemy tylko

TString size, insert, erase Dopiszmy metodę size() size_t size() const { return len; Czy potrafią Państwo zaimplementować kolejne pożyteczne metody: char* insert( size_t pos, const char* c ); char* insert( size_t pos, char c ); // insert zwraca pierwszy element wstawiony char* erase( size_t bpos = 0, size_t len = 0 ); // erase zwraca element za ostatnim usuniętym // ponownie wersja // uproszczona, powinny // być iteratory W programie powinno działać np: s1.insert( 0, "wstawka" ); s1.insert( s1.size(), 'a' ); s1.erase( 0, s1.size() / 2 ); // erase bez argumentów czyli erase(0,0) robi to co clear()

TString przykładowa implementacja insert char* TString::insert( size_t pos, const char* c ) { if (pos >=0 && pos <= len) { size_t oldlen = len; len = len+strlen(c); char* tmp = new char[ len+1 ]; strcpy( tmp, ptr ); for (size_t i=pos; i<pos+strlen(c); ++i) { tmp[i] = c[i-pos]; for (size_t i=pos; i<oldlen; ++i) { tmp[i+strlen(c)] = ptr[i]; delete [] ptr; ptr = tmp; return ptr+pos; else { throw out_of_range("zly argument"); return ptr; W programie powinno działać: s1.insert( 0, "napoczatek" ); s1.insert( s1.size(), "nakoniec" ); PROSZĘ SAMODZIELNIE ZAIMPLEMENTOWAĆ WERSJĘ Z ARGUMENTEM char c ORAZ METODĘ erase

TString jak wysłać obiekt do strumienia Aby możliwe było bezpośrednie wstawienie obiektu typu TString do strumienia, potrzeba przeciążyć operator<< operator<< ma dwa argumenty: strumień wyjściowy, obiekt TString Taka kolejność argumentów wymusza, że operator<< będzie funkcją globalną. Zdefiniujemy ją w pliku o nazwie np. operatory.cpp i dodatkowo zamkniemy w przestrzeni nazw MojeOperatory A gdzie deklaracja? Zwykle w jakimś pliku nagłówkowym, ale teraz friend poprzedzające deklarację funkcji (tutaj: globalnej) jest deklaracją przyjaźni konieczna, ponieważ zewnętrzna (tu: globalna) funkcja nie ma dostępu do części prywatnej klasy (czyli pól ptr i len ). Dopiero gdy klasa zadeklaruje przyjaźń, to jest dostęp. // w dowolnym miejscu wewnątrz klasy TString (deklaracja przyjaźni) friend std::ostream& MojeOperatory::operator<< ( std::ostream& strumien, const TString& s );

TString jak wysłać obiekt do strumienia Aby możliwe było bezpośrednie wstawienie obiektu typu TString do strumienia, potrzeba przeciążyć operator<< operator<< ma dwa argumenty: strumień wyjściowy, obiekt TString Taka kolejność argumentów wymusza, że operator<< będzie funkcją globalną. Zdefiniujemy ją w pliku o nazwie np. operatory.cpp i dodatkowo zamkniemy w przestrzeni nazw MojeOperatory A gdzie deklaracja? Zwykle pliku nagłówkowym: #ifndef OPERATORY_H #define OPERATORY_H #include <iostream> #include <fstream> class TString; // wystarczy deklaracja plik operatory.h namespace MojeOperatory { // definiujemy naszą przestrzeń nazw std::ostream& operator<<( std::ostream& strumien, const TString& s ); #endif

TString wysyłamy do strumienia, czytamy ze strumienia #include "tstring.h" #include "operatory.h" using namespace std; ostream& MojeOperatory::operator<<( ostream& strumien, const TString& s ) { return strumien << ( s.ptr? s.ptr : "pusty" ); plik operatory.cpp Proszę pamiętać o dopisaniu tego pliku (oprócz main.cc i tstring.cc) w linii kompilatora! // w programie teraz możemy bezpośrednio wysłać do strumienia // pamiętając o otwarciu naszej przestrzeni nazw!!! using namespace MojeOperatory; cout << "zawartość obiektu s5: " << s5 << endl; Aby możliwe było bezpośrednie czytanie strumienia do obiektu typu TString, potrzeba przeciążyć operator>> operator>> ma dwa argumenty: strumień wejściowy, obiekt TString bez przydomka const! Deklarujemy w przestrzeni nazw MojeOperatory, definiujemy w pliku operatory.cpp Pamiętajmy o deklaracji przyjaźni (wewnątrz klasy TString) friend std::istream& MojeOperatory::operator>>( std::istream& strumien, TString& s );

TString jak wczytać zawartość strumienia do obiektu? Idea czytania ze strumienia podobna do operacji przypisania, tzn. stara zawartość usuwana, a nowa umieszczana w odpowiednio pojemnej tablicy (do wskaźnika ptr). // uprościmy sobie zadanie, korzystając z tymczasowego obiektu string, dzięki // któremu nie będziemy się martwić o to, jak dużo wczytamy ze strumienia istream& MojeOperatory::operator>>( istream& strumien, TString& s ) { string tmp; // pamiętajmy o nagłówku <string> getline( strumien, tmp ); // wszystko najpierw ląduje do tmp // teraz trzeba przepisać delete [] s.ptr; s.len = tmp.length(); if ( s.len > 0 ) { s.ptr = new char[ s.len + 1 ]; strcpy( s.ptr, tmp.c_str() ); // metoda c_str() zwraca const char* z obiektu string else { s.ptr = nullptr; return strumien; Dlaczego obiekt typu std::string nie ma bezpośredniej konwersji do typu char* / const char* Ponieważ jeśli dzieje się to niejawnie, to jest to z reguły niepożądane!

TString dodajmy c_str() i pomyślmy o konwersji Dobrym pomysłem jest dodanie definicji metod c_str() w wersji zwykłej i stałej, na wzór metody istniejącej w klasie std::string // napiszmy te definicje szybko wewnątrz klasy TString, w części publicznej char* c_str() { return ptr; const char* c_str() const { return ptr; Przypomnijmy sobie, że w naszej klasie TString coś na kształt niejawnej konwersji już ma miejsce, spowodowane to jest istnieniem konstruktora o jednym argumencie. Wtedy program ma wiedzę jak zbudować TString z const char* // jak pamiętamy, możliwe jest TString s6 = "konwersja, ja nie jestem TString"; // by zablokować takie zjawisko, należy poprzedzić deklarację c-tor specyfikatorem explicit explicit TString( const char* c = nullptr ); Napiszmy teraz operator konwersji (metoda składowa w klasie TString): operator char*() { return ptr; // sprawdź też explicit operator char*(); operator const char*() const { return ptr; // j.w. z explicit // wtedy w programie możliwe jest również zapisanie np. char* ch = s6; char tabl[100]; strcpy( tabl, s6 ); proszę sprawdzić co się stanie z tymi przykładami gdy poprzedzimy powyższe deklaracje explicit A gdy mamy też użyty operator[] zobaczymy błąd.

TString implementacje paru składowych Często można kolejne metody implementować w oparciu o już napisaną. Np. // deklaracja w klasie TString char* insert( size_t pos, char c ); // implementacja w oparciu o insert( size_t, const char* ); char* TString::insert( size_t pos, char c ) { return insert( pos, string( { c ).c_str() ); // { uniwersalna inicjalizacja // metody push_back jedna z tych wspólnych metod kontenerów standardowych void push_back( char c ) { insert( len, c ); void push_back( const char* c ) { insert( len, c ); Dodajmy też licznik kontrolujący liczbę wyprodukowanych obiektów TString // deklaracja pola statycznego i definicja metody statycznej w klasie TString static size_t count; // w części prywatnej static size_t getn() { return count; // w części publicznej // gdzieś poza klasą np. przed main() definicja tej składowej statycznej size_t TString::count; Czy potrafisz teraz dopisać w odpowiednich funkcjach ++ i -- stanu zmiennej count?

TString przeciążmy jeszcze przydatne operatory + oraz += Zdeklarujmy (dwuargumentowy) operator+ oraz operator+= jako funkcje globalne: // deklaracja w przestrzeni nazw MojeOperatory TString operator+( const TString& a, const TString& b ); TString& operator+=( TString& a, const TString& b ); Proszę zwrócić uwagę na to, co zwraca + oraz co zwraca += // implementacja (w pliku operatory.cc) TString MojeOperatory::operator+( const TString& a, const TString& b ) { // musi powstać obiekt tymczasowy lokalny // proszę ponownie zaimplementować za pomocą insert TString& MojeOperatory::operator+=( TString& a, const TString& b ) { return ; // zapiszmy w jednej linijce, korzystając z + Oczywiście napiszmy również kilka linii testów w programie cout << s1+s2 << endl; // obserwujmy komunikaty konstruktorów i destruktorów s1 = s1 + "ten argument niedopasowany, a dziala"; s1 += s1; // dodanie do samego siebie

TString przeciążmy jeszcze przydatne operatory + oraz += Zdeklarujmy (dwuargumentowy) operator+ oraz operator+= jako funkcje globalne: // deklaracja w przestrzeni nazw MojeOperatory TString operator+( const TString& a, const TString& b ); TString& operator+=( TString& a, const TString& b ); Proszę zwrócić uwagę na to, co zwraca + oraz co zwraca += // implementacja (w pliku operatory.cc) TString MojeOperatory::operator+( const TString& a, const TString& b ) { TString tmp ( a ); tmp.insert( tmp.size(), b ); // konwersja albo trzeba b.c_str() jako drugi argument return tmp; TString& MojeOperatory::operator+=( TString& a, const TString& b ) { return a = a + b; Oczywiście napiszmy również kilka linii testów w programie cout << s1+s2 << endl; // obserwujmy komunikaty konstruktorów i destruktorów s1 = s1 + "ten argument niedopasowany, a dziala"; s1 += s1; // dodanie do samego siebie

TString obiekt funkcyjny Bardzo ważną i efektywną częścią programowania jest używanie / pisanie obiektów funkcyjnych. W roli takich obiektów najczęściej występują klasy (struktury) mające przeciążony operator oraz tzw. wyrażenia lambda. Przeciążmy operator() // musi to być składowa klasy, zatem w części publicznej: void operator()( const char& c ) { push_back( c ); // może robić cokolwiek, ja wymyśliłem, że będzie wstawiać na koniec pojemnika // użycie takiego operatora wygląda jak wywołanie funkcji ale używamy obiektu: s6('a'); //całość wygląda jak wywołanie metody s6(char), ale s6 to obiekt TString // w rzeczywistości jest to takie wywołanie: s6.operator()( A ); Ciekawsze będzie jednak użycie algorytmu np. przepiszmy z obiektu typu std::string literka po literce, wstawiając do obiektu TString. Użyjemy for_each // uwaga! potrzebny nagłówek <algorithm> string ss("abcdefghijkl"); s2 = for_each(ss.begin(), ss.end(), s2 ); // proszę sprawdzić co się stanie jeśli nie będzie s2 = cout << s2 << endl; // algorytm for_each przenosi s2 więc bez s2 = zostałoby puste!