70 likes | 273 Views
Klasy Do struktury można dołączyć operacje działające na jej polach. struct date { int day, month, year; void set (int d, int m, int y); int less (date d); void print ( ); }; Funkcje zadeklarowane w środku struktury nazywamy funkcjami składowymi (member functions). Wywołanie:
E N D
Klasy • Do struktury można dołączyć operacje działające na jej polach. • struct date { • int day, month, year; • void set (int d, int m, int y); • int less (date d); • void print ( ); • }; • Funkcje zadeklarowane w środku struktury nazywamy funkcjami • składowymi (member functions). • Wywołanie: • date today, end_of_year; • void f () • { • today.set (8, 12, 2003 ); • today.print (); • end_of_year.set (31, 12, 2003); • end_of_year.print (); • if (today.less(end_of_year)) • { ..... } • } • Mechanizm wygodny, ale nieobiektowy. • Klasy różnią się od struktur tylko ochroną atrybutów.
class date { • public: • void set (int d, int m, int y); • int less(date d); • void print ( ); • private: • int day, month, year; • } • Prywatna część klasy (w tym przypadku atrybuty obiektów klasy) • może być używana wyłącznie przez funkcje składowe. Nie ma • dostępu przez kropkę z zewnątrz do tych atrybutów. • Publiczna część klasy stanowi interfejs obiektów klasy • widoczny przez świat zewnętrzny. • Definicje funkcji składowych (muszą gdzieś wystąpić): • void date::set (int d, int m, int y) • { • day = d; month = m; year = y; • } • int date::less (date d) • { // && ma wyższy priorytet niż || • return ( year < d.year) || (year == d.year) && (month < d.month) • || (year == d.year) && (month == d.month) && (day < d.day); • } • void date::print () • { • cout << day << “ - “ << month << “ - “ << year; • } • void f () • date today, end_of_year; • { • today.set (8, 12, 2003); ..... • }
Każda funkcja składowa może odwołać się do obiektu, w którym się • oblicza (do którego została wysłana przez kropkę). • this wskaźnik do obiektu, w którym jesteśmy • Jeśli this użyty jest w obiekcie klasy date, to ma typ date * const. • Tworzenie i usuwanie obiektu klasy • Obiekt klasy może powstać: • w momencie powstania zmiennej typu tej klasy (przy jej deklaracji) • przez dynamiczne utworzenie go przez new • date dzisiaj; // teraz powstaje obiekt klasy date • date *jutro; // tu nie ma jeszcze obiektu klasy date • ..... • jutro = new date; // dynamiczne powstanie obiektu klasy date • Obiekt, który powstał automatycznie ginie, kiedy ginie zmienna, • której był wartością (np. po zakończeniu funkcji, w której zmienna była • zadeklarowana). • Obiekt, który powstał na skutek wykonania new, ginie przez • wykonanie na nim delete. • Konstruktory / destruktory. • class date { • public: • date (int d, int m, int y); // konstruktor inicjujący wszystko • date (char * s); // robi konwersję z napisu • date (int number); // number - zakodowana data • date (); // bez inicjalizacji • ~date (); { } // destruktor - pusty • ...... // funkcje set, less, print • } • date::date(int d, int m, int y ) • { • day = d; month = m; year = y; • }
Konstruktor obiektu (zdefiniowany w klasie) jest automatycznie • wykonywany w momencie powstania obiektu tej klasy (inicjalizacja). • Destruktor jest automatycznie wykonywany w momencie ginięcia • obiektu. • Na ogół konstruktory są przeciążone, tzn. mamy różne konstruktory, • dla różnych sposobów inicjalizacji obiektu. • date jutro = date ( 8, 12, 2003); • date today (8, 12, 2003); // skrócona forma • date sylwester (“31 Grudnia 2003”); • date kiedys; // bez inicjalizacji • date kto_to_wie (37963); // dzień o numerze 37963 • // liczonym od 01-01-1900 • date * koniec = new data (8, 12, 2003); • W definicji klasy często stosujemy funkcje wstawiane (inline), • jeżeli funkcje składowe są krótkie. Funkcja składowa jest wstawiana, • jeżeli przy deklaracji podajemy od razu jej treść. • Przykład zastosowania destruktora: • class char_stack { • private: • int size; • char *top; • char *s; • public: • char_stack (int sz) { top = s = new char [size = sz]; } • ~char_stack ( ) { delete[] s; } // destruktor • void push (char c ) { *top++ = c; } • char pop ( ) { return * --top; } • }; • void f ( ) • { • char_stack s1 (100); • ...... • } // po zakończeniu funkcji stos (tablica) zostanie zniszczony
Statyczne składniki (atrybuty i funkcje) klas • Statyczne zmienne będące składnikami klasy są odpowiednikami • zmiennych klasowych - są atrybutami klasy, a nie obiektu (są • współdzielone przez wszystkie obiekty danej klasy). • Statyczne funkcje składowe są odpowiednikami metod klasowych. • Są wykonywane w klasie, a nie w obiekcie. Mogą działać tylko na • statycznych atrybutach klasy. • class Rachunek { • public: • Rachunek ( int stan_początkowy = 0) // domyślny argument • { stan = stan_początkowy; • suma_rachunków += stan; } • void wpłać ( int kwota ) • { stan += kwota; suma_rachunków += kwota;} • void wypłać (int kwota ) • { stan -= kwota; suma_rachunków -= kwota; } • static int daj_sumę ( ) • { return suma_rachunków; } • private: • static int suma_rachunków; // suma stanu wszystkich rach. • int stan; // stan tego rachunku • }; • int Rachunek::suma_rachunków = 0; • void f ( ) • { • Rachunek r1(300), r2, r3(500); • r1.wpłać (400); .... • cout << “Suma wszystkich” << Rachunek :: daj_sumę(); • }
Konstruktor kopiujący • class Napis • { • private: • char *ptr; • int len; • public: • Napis () { ptr = NULL; len = 0; } • Napis (char * str ) • { • len = strlen (str); • ptr = new char [len +1]; • strcpy (ptr, str); • } • ~ Napis () { delete[] ptr; } • }; • void fun ( ) • { • Napis a (“Test”); • Napis b = a; // kopiowanie obiektu a na b • ...... • } • Po zakończeniu funkcji fun zostanie 2 razy wykonany destruktor. • Dwa razy zwolni się pamięć na string ptr - błąd! • Ten sam problem powstaje przy przypisaniu b = a. • Konstruktor kopiujący jest wołany przy inicjacji zmiennej • innym obiektem lub przy przekazywaniu parametrów lub • wyników. Domyślny konstruktor kopiujący - kopiuje obiekt • składowa po składowej. • Napis (const Napis & obj) • { • len = strlen (obj.ptr); ptr = new char [len + 1]; • strcpy (ptr, obj.ptr); • }
Zagnieżdżanie • Nie można zagnieżdżać funkcji • Można zagnieżdżać klasy/struktury (w klasach i strukturach), ale z ograniczeniami: dostęp tylko do składowych statycznych (nazw typów, wyliczeń). • Można też deklarować klasy lokalne w funkcjach (są widoczne _tylko_ wewnątrz obejmującej funkcji, nie mogą więc mieć składowych statycznych) int f() { int k; void f1(){} // Źle, nie wolno zagnieżdżać funkcji struct inner // OK, można definiować klasy/struktury lokalne { int i; void f2(int); // Źle, musi być od razu definicja void f3(){} // Ale to już OK void f4(){return k;} // To też błąd }; // ... }