OOPC++ - wstêp, klasy1 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.
OOPC++ - wstêp, klasy2 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);..... }
OOPC++ - wstêp, klasy3 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ę). thiswskaź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; }
OOPC++ - wstêp, klasy4 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 // liczonym od 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
OOPC++ - wstêp, klasy5 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ę(); }
OOPC++ - wstêp, klasy6 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); }
OOPC++ - wstêp, klasy7 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 }; //... }