Języki Programowania 5From MJanik(Difference between revisions)
Latest revision as of 09:16, 28 October 2025Zadanie 5 - Klasy, enkapsulacja, domyślne wartości, podział na pliki
Poniżej zadanie przykładowe do przećwiczenia. Zadanie właściwe zostanie udostępnione na zajęciach.
WstępKrotki opis pomysłu: Tworzymy oprogramowanie dla firmy. Firma ma pracowników zajmujących standardowo jedno z trzech stanowisk: informatyk, menadżer albo sprzątaczka. Firma posiada również flotę służbowych samochodów, nieprzyporządkowanych do pracowników. Chcielibyśmy posiadać "bazę danych" zarówno pracowników i samochodów, oraz posiadać łatwą możliwość obliczenia kosztów utrzymania wszystkiego.
1. Krok pierwszy: pola prywatne i publiczne, wskaźnik "this", tworzenie klasTworzymy potrzebne klasy, umożliwiające dodawanie i wypisywanie zarówno samochodów jak i pracowników. Jeśli czujesz, że potrzebujesz przećwiczyć tworzenie klas, możesz najpierw spróbować wykonać te przykładowe zadania. Ale zanim się do tego zabierzemy, zacznijmy od garści informacji: Pola prywatne i publiczne: Enkapsulacja (ang. encapsulation) to jedna z podstawowych zasad programowania obiektowego. Polega na ukrywaniu wewnętrznej budowy obiektu (czyli danych i sposobu ich przetwarzania) przed światem zewnętrznym oraz udostępnianiu tylko ściśle określonego interfejsu — zwykle w postaci metod publicznych. W praktyce oznacza to, że: • dane obiektu (pola klasy) są prywatne (private), • dostęp do nich odbywa się tylko przez publiczne metody (public), które kontrolują sposób odczytu i modyfikacji danych. prywatne: private Jest dostępne tylko dla funkcji składowych klasy (i funkcji zaprzyjaźnionych z daną klasą). Jeśli nie wyszczególnimy etykiety, to wszystkie składniki klasy będą domyślnie prywatne. publiczne: public Publiczne składniki mogą być używane zarówno we wnętrzu klasy, jak również spoza jej zakresu.
- Mówi: "teraz odwołasz sie do składowej >TEJ< klasy" (w której jesteś) - Jego używanie nie jest obligatoryjne (przydaje sie, jeśli z jakiegoś powodu chcemy posiadać takie same nazwy dla argumentów funkcji jak i składników klasy) Potrzebne klasy: Klasa Pracownik: pola prywatne: string imie; string nazwisko; int wiek; double pensja; string zawod; metody publiczne: void zapisz(string i, string n, int w, double p, string z); void wypisz(); Klasa samochod: pola prywatne: rodzaj marka; double spalanie_na_kilometr; double km; metody publiczne: void zapisz(rodzaj marka, double spalanie_na_kilometr, double km); //uwaga, te same nazwy! żeby moc przyporządkować odpowiednie wartości - użycie wskaźnika this! "this->km = km;" - przyporządkuje do <składnika klasy km> wartość <argumentu funkcji km>) void wypisz(); By przetestować działanie programu, tworzymy w funkcji main() tablice pracowników i samochodów, oraz tworzymy np. 3 samochody i 3 pracowników. Wypisujemy ich na ekran. 2. Krok drugi: enumTypy wyliczeniowe: enum. Jest to osobny typ dla wybranego zestawu stałych całkowitych. Przydaje sie często - np. mamy ograniczana liczbę stanowisk, na jakich może być zatrudniony pracownik (w naszym przypadku: 3). Ale zapamiętywanie każdego zawodu jako "string" nie dość, ze zajmuje niepotrzebna bardzo dużo miejsca, to jeszcze sprzyja generowaniu problemów (np. jeden programista wpisze "Sprzataczka" drugi "sprzataczka", a trzeci, mający brata pracującego przy sprzątaniu wpisze "sprzatacz". A wtedy zaczynają sie problemy z porównywaniem składnika "zawod" z jakimś konkretnym słowem... Wiec myślimy: skoro mamy trzy zawody, to przypiszmy im numery. Informayk - 1, sprzątaczka - 2, manager - 3. I po problemie. Jednak zapamiętywanie, która cyferka odpowiada jakiemu zawodowi może również generować problemy, chociaż innego rodzaju - w dużych programach mamy tysiące linijek kodu. Wyobraźmy sobie, ze musimy dodać nowa funkcje. A kto będzie pamiętał po tygodniu, czy 3 to była sprzątaczka, czy manager... Używamy wiec typu wyliczeniowego enum: Przykład użycia: enum zawod{ informatyk=1, sprzataczka, manager}; W ten sposób, jeśli zadeklarujemy taki typ globalnie, to za każdym razem, jeśli napiszemy w kodzie "manager" to program będzie wiedział, ze chodzi o 3. A jeśli programista próbował by napisać np. "Manager" to od razu kompilator zgłosi błąd. Oczywiście powodów użycia typów wyliczeniowych możemy wymyślać dużo więcej. Zamiast deklarować typu wyliczeniowego globalnie, możemy zrobić to również wewnątrz klasy. Takie postępowanie jest bardzo naturalne - niektóre wyliczenia wiążą sie tylko i wyłącznie z jedną, konkretną klasą. Wtedy możemy zadeklarować: class Pracownik{
public:
enum zawod{ informatyk=1, sprzataczka, menadzer};
...
}
Wtedy wewnątrz składników klasy używamy zwyczajnie slow "informatyk" tak, jakby był to dowolny int. Na zewnątrz klasy musimy jednak określić, z jakiego zakresu słowo "informatyk" ma pochodzić: Pracownik::informatyk. Wydawało by sie, ze łatwiej zapamiętać cyfrę 1, niż złożoną konstrukcję "Pracownik::informatyk". Ale to niekoniecznie prawda - jeśli przeglądamy kod jakiś czas po napisaniu programu i widzimy w nim masę 1, 2 i 3 - to niewiele jesteśmy w stanie z niego przeczytać. Jeśli natomiast mamy w takich miejscach "Pracownik::informatyk" kod staje sie dla nas dużo bardziej czytelny i przejrzysty - jesteśmy w stanie od reki odpowiedzieć na pytanie, czego dokładnie dotyczy dana linijka. Zadanie: - zmodyfikować markę samochodu tak, by była globalnym typem wyliczeniowym (przyjmijmy, ze firma kupuje jedynie ople, mazdy i toyoty). - zmodyfikować zawód pracownika tak, by był typem wyliczeniowym (informatyk, sprzataczka, manager) w zakresie klasy Pracownik (oczywiście publicznym). Wskazówka: Zamiast: void zapisz(string i, string n, int w, double p, string z); Będziemy mieli: void zapisz(string i, string n, int w, double p, zawod z); Zamiast: flota[0].zapisz("Opel",7,200); Będziemy mieli: flota[0].zapisz(Opel,7,200); Zamiast: personel[0].zapisz("Jan","Kowalski",43,1000.0,"informatyk"); Będziemy mieli: personel[0].zapisz("Jan","Kowalski",43,1000.0,Pracownik::informatyk); Sprawdzić czy działa!
3. Krok trzeci: Domyslne wartosci argumentow funkcjiTo jest niesamowicie proste i przydatne. Powiedzmy, ze tworząc (zapisując) samochod, zazwyczaj nasza firma kupuje samochody spalajace 7 l/km, a gdy są nowe, to ich przebieg wynosi 0. Modyfikujemy: void zapisz(rodzaj marka, double spalanie_na_kilometr, double km) Dodając "=<jakaś liczba>" przy argumentach funkcji void zapisz(rodzaj marka, double spalanie_na_kilometr=7, double km=0) Wtedy w funkcji main jesli tworzymy samochod, mozemy napisać: zapisz(opel, 7, 0); albo zapisz(opel, 7); - wpisalismy jeden argument mniej albo zapisz(opel); - wpisalismy dwa argumenty mniej albo tez zmienic argumenty: zapisz(opel, 6, 5000);
Bardzo często i powszechnie stosowane. Zapamiętać!
4. Krok czwarty: Podział na plikiNa koniec: Podzielić nasz program na oddzielne klasy: główną (main.cpp), dla pracownika (pracownik.cpp, pracownik.h) oraz dla samochodu (samochod.cpp, samochod.h) - uwaga, pewne modyfikacje mogą być konieczne!
Dodatkowo: zapisywanie do plikuNapisz funkcję składową, która zapisuje wszystkie informacje o samochodzie do pliku samochod.txt (w takim samym formacie jak funkcja wypisz()). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||