Programowanie obiektowe w C++
PROGRAMOWANIE OBIEKTOWE
Klasa jest typem danych, będącym czymś w rodzaju struktury rozbudowanej o funkcje składowe. Obiektem nazywamy konkretny egzemplarz danej klasy. W skład klasy wchodzą: dane (zmienne) składowe, funkcje składowe (metody), w tym konstruktory i destruktory. Klasę deklaruje się przy użyciu słowa kluczowego class, np: class Nazwaklasy;. W dobrym stylu jest nadawanie klasie nazwy z dużej litery. Deklaracja klasy jest zawarta w nawiasach klamrowych {}, musi po niej wystąpić instrukcja pusta ‘;’ (inaczej niż np. przy definicji funkcji).
Elementy klasy są podzielone na dwie sekcje: publiczną i prywatną. Elementy te mogą być deklarowane w dowolnej liczbie występujących po sobie sekcji wydzielonych przez etykietę public: lub private:. Elementy klasy są domyślnie prywatne. Metod i zmiennych publicznych obiektu można używać wszędzie w programie, natomiast do metod i zmiennych prywatnych dostęp mają tylko inne metody klasy.
Klasa powinna być abstrakcyjnym typem danych, czyli takim, który raz zaprojektowany może być używany przez innych programistów bez konieczności wgłębiania się w jego mechanizm. Sekcja publiczna deklaracji klasy nazywana jest interfejsem klasy. zawarte w niej metody wraz z komentarzami opisującymi ich parametry, działanie i format wyniku powinny dawać korzystającemu z niej programiście rozsądne maksimum informacji na temat klasy (bez wchodzenia w szczegóły implementacyjne). W myśl zasady abstrakcyjności danych, wszystkie dane składowe klasy powinny znajdować się w sekcji prywatnej. W sekcji publicznej zaś należy umieszczać metody odpowiednio operujące na danych prywatnych. Można wyróżnić dwa rodzaje metod publicznych: akcesory (funkcje dostępu) i modyfikatory (funkcje modyfikacji). Dobrze jest nadawać im nazwy zgodnie z następującą konwencją: dla modyfikatorów set_wartosc, dla akcesorów get_wartosc. Modyfikatory najczęściej nie zwracają wartości, a akcesory są bezparametrowe. Jeśli metoda publiczna wykonuje wiele skomplikowanych obliczeń lub pewne z nich wykonywane są przez wiele metod publicznych, to można je wyodrębnić w postaci osobnej metody prywatnej. Są one wtedy wyłącznie na użytek implementacji klasy. Implementacja klasy powinna być zaprojektowana przez programistę w sposób, który daje mu możliwość rozbudowania i udoskonalania działania funkcji jej składowych, bez wprowadzania jakichkolwiek zmian w interfejsie.
Każda klasa posiada domyślnie dwie specjalne funkcje składowe - konstruktor i destruktor. Konstruktor wywoływany jest, gdy tworzymy nowy obiekt na stercie : Pies *reksio = new Pies(); lub gdy tworzymy nowy obiekt na stosie: Pies Reksio = Pies();. Destruktor wywoływany jest automatycznie gdy usuwamy obiekt ze sterty: delete reksio(); lub gdy program opuszcza blok w którym zadeklarowany (na stosie) był obiekt; Konstruktory można przeciążać. Należy jednak pamiętać, że jeśli zadeklarujemy jakikolwiek konstruktor pobierający parametry, musimy także zadeklarować konstruktor domyślny (bezparametrowy).
Dostęp do danych i funkcji składowych obiektu uzyskujemy przez użycie operatora dostępu: ‘.’, a jeśli mamy do czynienia z obiektem na stercie, czyli kiedy operujemy wskaźnikiem do obiektu, możemy zamiast np. *(reksio).wiek(); użyć operatora ‘->’:
reksio->wiek();
Przy definiowaniu funkcji składowej funkcja1 poza deklaracją klasy JakasKlasa, piszemy: JakasKlasa::funkcja1(argumenty). Konstruktory i destruktory definiuje się inaczej. Konstruktor musi nosić nazwę identyczną z nazwą klasy, destruktor podobnie, z tą różnicą, że należy ją poprzedzić znakiem tyldy ‘~’. Konstruktory i destruktory definiujemy bez używania operatora przynależności ‘::’. Przy definicji konstruktora domyślnego przydane jest inicjalizowanie danych. Sekcja inicjalizacji jest zawarta pomiędzy nawiasem zamykającym listę argumentów konstruktora i nawiasem klamrowym otwierającym jego blok definicji. Aby zainicjalizować zmienne składowe zmienna1 i zmienna2 wartościami 100 i "Ania" należy umieścić w sekcji inicjalizacji wpis: zmienna1(100), zmienna2("Ania").
Dla upewnienia się, że funkcja składowa nie zmienia żadnej z wartości składowych, umieszcza się słowo kluczowe const przed instrukcją pustą w linii zawierającej jej deklarację.
Aby zoptymalizować działanie programu, możemy krótkie i często powtarzające się w implementacji klasy ciągi instrukcji wyodrębnić jako osobną funkcję inline, np. inline int funkcja1(argumenty);, wtedy wszędzie, gdzie kompilator napotka wpis funkcja1(argumenty); zostanie wstawiony blok definicji funkcji funkcja1.
Danymi składowymi klasy mogą być naturalnie elementy innej klasy, jak również argumentami i wartościami zwracanymi przez metodę klasy mogą być obiekty tej lub innej klasy. Każdy obiekt klasy posiada domyślnie wskaźnik do samego siebie - this. Przydaje się on, np. gdy chcemy, aby funkcja składowa zwracała obiekt do którego przynależy (retun this;). Jeśli chcemy nadawać obiektowi wiele różnych atrybutów naraz poprzez funkcje modyfikacji, możemy to zrobić pisząc np. kolezanka.set_wiek(20).set_imie("Maja"); , pod warunkiem, że funkcje składowe klasy o nazwach wiek i imie zwracają obiekt typu tej klasy o wartości this. Zabieg ten nazywamy method-chainingiem.
Sekcje deklaracji klasy dobrze jest umieścić w osobnym pliku niż sekcję implementacji. Plik taki nazywamy plikiem nagłówkowym, powinien on posiadać rozszerzenie .hpp. Dołącza się go do pliku z implementacją poprzez dyrektywę #include "plik.hpp".