Programowanie w C++ Wykład 11 Katarzyna Grzelak 13 maja 2019 K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30
Klasy cd K.Grzelak (Wykład 11) Programowanie w C++ 2 / 30
Klasy - powtórzenie Klasy typy definiowane przez użytkownika Klasy zawieraja dane składowe plus funkcje składowe (metody) K.Grzelak (Wykład 11) Programowanie w C++ 3 / 30
Podział na pliki.h i.cc Każda klasa w osobnym pliku (na ogół) W pliku nagłówkowym (rozszerzenie.h) definicja klasy W pliku.cc (lub.cpp,.cxx... ) definicje funkcji składowych K.Grzelak (Wykład 11) Programowanie w C++ 4 / 30
Plik nagłówkowy z definicja klasy Przykład: Wektor2D.h #ifndef WEKTOR2D_H #define WEKTOR2D_H class Wektor2D { public: Wektor2D(){} // konstruktor standardowy Wektor2D(double a1,double b1,double a2,double b2): x1(a1),y1(b1),x2(a2),y2(b2) { } // drugi konstr Wektor2D() { } // destruktor void ustaw(double a1, double b1, double a2, double b2); double dlugosc(); private: double x1,y1,x2,y2; }; #endif K.Grzelak (Wykład 11) Programowanie w C++ 5 / 30
Plik z definicjami funkcji składowych Przykład: Wektor2D.cc #include<cmath> #include "Wektor2D.h" void Wektor2D::ustaw(double a1, double b1, double a2, double b2){ x1=a1; y1=b1; x2=a2; y2=b2; } double Wektor2D::dlugosc(){ double d=sqrt((x2-x1)*(x2-x1) +(y2-y1)*(y2-y1)); return d; } K.Grzelak (Wykład 11) Programowanie w C++ 6 / 30
Główny program, użycie nowego typu zmiennej Przykład: prog.cc #include<iostream> #include "Wektor2D.h" using namespace std; int main(){ Wektor2D v1; v1.ustaw(0.5,0.2,1.5,0.8); Wektor2D v2(0.6,0.3,1.5,1.8); cout «v1.dlugosc() «endl; cout «v2.dlugosc() «endl; return 0; } K.Grzelak (Wykład 11) Programowanie w C++ 7 / 30
Przekazywanie obiektów danej klasy do funkcji...... w taki sam sposób jak w przypadku obiektów typu int, double,... : przez wartość (nie polecany sposób, bo kopiowanie potencjalnie dużych obiektów): void (Wektor2D v1); przez referencję (jeśli funkcja ma zmieniać obiekt): void (Wektor2D &v1); przez referencję (jeśli funkcja ma nie zmieniać obiektu): void (const Wektor2D &v1); K.Grzelak (Wykład 11) Programowanie w C++ 8 / 30
Funkcje składowe typu const Funkcja składowa z przydomkiem const gwarantuje, że jeśli będzie wywołana na rzecz jakiegoś obiektu, to go nie zmodyfikuje. Np. Funkcje zwracajace wartości danych składowych klasy powinny mieć przydomek const. double getx1() const; K.Grzelak (Wykład 11) Programowanie w C++ 9 / 30
Przyjaciele - Friends Klasa może zadeklarować kto jest jej przyjacielem = ma dostęp do prywatnych składników klasy. Konsystentniej deklarować przyjaciół w części publicznej. funkcje friend: class A{ //... friend double B::fun(const A& a); friend void globfun(const A& a); //... }; K.Grzelak (Wykład 11) Programowanie w C++ 10 / 30
Przyjaciele - Friends Klasa może zadeklarować kto jest jej przyjacielem = ma dostęp do prywatnych składników klasy. Konsystentniej deklarować przyjaciół w części publicznej. klasy friend: class A{ //... friend class B; }; K.Grzelak (Wykład 11) Programowanie w C++ 11 / 30
Konstruktor i destruktor Konstruktor to specjalna funkcja składowa wywoływana automatycznie przy tworzeniu obiektu, a destruktor funkcja składowa wywoływana przed likwidacja obiektu. Konstruktor nazywa się tak samo jak klasa Destruktor nazywa się tak samo jak klasa + falka przed nazwa. Przed nazwami konstruktora i destruktora nie ma żadnego typu wartości zwracanej; nie ma return K.Grzelak (Wykład 11) Programowanie w C++ 12 / 30
Konstruktor i destruktor Może być wiele konstruktorów, ale destruktor co najwyżej jeden. Konstruktor służy do inicjalizacji obiektu W treści programu nie widać że będzie wywoływany destruktor. Destruktor najczęściej używany gdy trzeba zwolnić pamięć ( operator delete ) K.Grzelak (Wykład 11) Programowanie w C++ 13 / 30
Konstruktor kopiujacy Konstruktor kopiujacy to specjalna funkcja składowa wywoływana gdy jest robiona kopia obiektu (jawnie lub niejawnie). Jawne kopiowanie: Wektor2D v1; Wektor2D v2(v1); Wektor2D v3=v1; Uwaga: W poniższej sytuacji NIE jest wywoływany konstruktor kopiujacy: Wektor2D v1(0.5,0.2,1.5,0.8); Wektor2D v2; v2=v1; K.Grzelak (Wykład 11) Programowanie w C++ 14 / 30
Konstruktor kopiujacy Konstruktor kopiujacy to specjalna funkcja składowa wywoływana gdy jest robiona kopia obiektu (jawnie lub niejawnie). Niejawne kopiowanie: w przypadku przesyłania argumentów do funkcji przez wartość, gdy funkcja zwraca obiekt przez wartość, wypełnianie vector a STL... K.Grzelak (Wykład 11) Programowanie w C++ 15 / 30
Konstruktor kopiujacy dla klasy Wektor2D } Wektor2D (const Wektor2D & source){ x1=source.x1; y1=source.y1; x2=source.x2; y2=source.y2; K.Grzelak (Wykład 11) Programowanie w C++ 16 / 30
Automatycznie generowany konstruktor, destruktor, konstruktor kopiujacy Jeżeli nie zdefiniujemy standardowego konstruktora, konstruktora kopiujacego lub destruktora na ogół zostana stworzone automatycznie. W wielu przypadkach jest to wystarczajace. Konstruktor kopiujacy jest najczęściej potrzebny gdy składnikiem klasy jest wskaźnik. K.Grzelak (Wykład 11) Programowanie w C++ 17 / 30
Przeładowanie operatorów K.Grzelak (Wykład 11) Programowanie w C++ 18 / 30
Przeładowanie operatorów Przeładowanie operatorów to przedefiniowanie standardowych operatorów: +,,, /,... tak, żeby działały dla obiektów danej klasy. Przykład Klasa Ulamek, reprezentujaca ułamek zwykły. Dodajemy dwa ułamki: Ulamek u3= dodaj(u1,u2); Lepiej zdefiniować własny (przeładowany) operator + Ulamek u3=u1+u2; K.Grzelak (Wykład 11) Programowanie w C++ 19 / 30
Przeładowanie operatora dodawania Definiowanie własnego operatora (np. dodawania) dla danej klasy = definiowanie nowej funkcji o nazwie operator+ Funkcja musi mieć nazwę operator+ Co najmniej jednym z argumentów nowej funkcji operator+ musi być obiekt danej klasy Gdy napiszemy Ulamek u=u1+u2; funkcja operator+ zostanie uruchomiona automatycznie Funkcja operator+ może być funkcja składowa klasy albo funkcja globalna K.Grzelak (Wykład 11) Programowanie w C++ 20 / 30
Fragmenty definicji klasy Ulamek gdy operator+ jest funkcja składowa klasy Ulamek: class Ulamek{ public: Ulamek(); Ulamek(int licznik, int mianownik); Ulamek operator+(const Ulamek& v1); void Wypisz(); private: int licznik; int mianownik; }; K.Grzelak (Wykład 11) Programowanie w C++ 21 / 30
Przeładowanie operatora dodawania klasy Ulamek gdy operator+ jest funkcja składowa klasy Ulamek: Ulamek Ulamek::operator+(const Ulamek & u1){ Ulamek u; u.licznik=licznik*u1.mianownik+u1.licznik*mianow u.mianownik=mianownik*u1.mianownik; return u; } Ulamek u1(1,2),u2(1,3); Od teraz: Ulamek u=u1+u2; Taka funkcję można też wywołać jak zwykła funkcję: Ulamek u = u1.operator+(u2); K.Grzelak (Wykład 11) Programowanie w C++ 22 / 30
Fragmenty definicji klasy Ulamek gdy operator+ jest funkcja globalna: class Ulamek{ public: Ulamek(); Ulamek(int licznik, int mianownik); void Wypisz(); friend Ulamek operator+(const Ulamek& u1, const Ulamek& u2); private: int licznik; int mianownik; }; K.Grzelak (Wykład 11) Programowanie w C++ 23 / 30
Przeładowanie operatora dodawania klasy Ulamek gdy operator+ jest funkcja globalna: Ulamek operator+(const Ulamek& u1, const Ulamek& u2){ Ulamek u; u.licznik=u1.licznik*u2.mianownik+u2.licznik*u1. u.mianownik=u1.mianownik*u2.mianownik; return u; } Ulamek u1(1,2),u2(1,3); Od teraz: Ulamek u=u1+u2; Taka funkcję można też wywołać jak zwykła funkcję: Ulamek u = operator+(u1,u2); K.Grzelak (Wykład 11) Programowanie w C++ 24 / 30
Jakie operatory można przedefiniować? Większość operatorów można przedefiniować Nie można przedefiniować:. :: Nie można przedefiniować symboli preprocesora # ## Nie można tworzyć nowych operatorów K.Grzelak (Wykład 11) Programowanie w C++ 25 / 30
Fragmenty definicji klasy Ulamek Jeśli ten sam operator jest zdefiniowany dwa razy : jako składowa klasy i jako funkcja globalna, to pierwszeństwo ma funkcja składowa klasy. class Ulamek{ public: Ulamek(); Ulamek(int licznik, int mianownik); void Wypisz(); Ulamek operator+(const Ulamek& v1); friend Ulamek operator+(const Ulamek& u1, const Ulamek& u2); friend ostream& operator«(ostream& os, const Ulamek& u1); friend istream& operator»(istream& is, Ulamek& u1); private: K.Grzelak (Wykład 11) Programowanie w C++ 26 / 30
Przeładowanie operatora «Wypisywanie zwykłego ułamka na ekran, dwie możliwości: 1) Zwykła funkcja Wypisz: void Wypisz(){ cout «licznik «/ «mianownik «endl; } 2) Przeładowany operator «ostream& operator«(ostream& os, const Ulamek& u1){ os «u1.licznik «/ «u1.mianownik «endl; return os; } K.Grzelak (Wykład 11) Programowanie w C++ 27 / 30
Przeładowanie operatora «Jeżeli operator «jest przeładowany, można napisać tak: Ulamek us3(5,4); cout «suma3= «us3 «endl; (Można też zawołać jak zwykła funkcję:) operator«(cout,us3); K.Grzelak (Wykład 11) Programowanie w C++ 28 / 30
Przeładowanie operatora» istream& operator»(istream& is, Ulamek& u1){ cout «Podaj ulamek «endl; cout «Licznik: ; is» u1.licznik; cout «Mianownik: ; is» u1.mianownik; if (!is) return is; } K.Grzelak (Wykład 11) Programowanie w C++ 29 / 30
Na koniec ciekawostka - cout Zapis którego standardowo używamy: int a=15; cout «a; Zapis funkcyjny: int a=15; cout.operator«(a); K.Grzelak (Wykład 11) Programowanie w C++ 30 / 30