Wykład 3
Transkrypt
Wykład 3
Programowanie obiektowe w C++ Wykład 2 dr Lidia Stępień Akademia im. Jana Długosza w Częstochowie L. Stępień (AJD) POwCPP 1 / 32 Programowanie proceduralne Problem dzielony jest na mniejsze fragmenty, zwane modułami. Programista decyduje jakie chce mieć funkcje(procedury). Wybiera najlepsze algorytmy. Projektuje struktury do przechowywania danych. L. Stępień (AJD) POwCPP 2 / 32 Programowanie obiektowe Zdecyduj, jakie chcesz mieć klasy; dla każdej klasy dostarcz pełny zbiór operacji; korzystając z mechanizmu dziedziczenia jawnie wskaż, co jest wspólne. Cechy języka programowania obiektowego: abstrakcyjne typy danych (klasy) hermetyzacja danych (ukrywanie) dziedziczenie polimorfizm L. Stępień (AJD) POwCPP 3 / 32 Hermetyzacja (ang. encapsulation) Kapsułkowanie, enkapsulacja - ograniczenie dostępności danych i funkcji wewnętrznych klas i obiektów, udostępnianie ich jedynie za pomocą specjalnych funkcji nazywanych metodami. Polimorfizm Wielopostaciowość - możliwość istnienia wielu metod o tej samej nazwie, powiązana z możliwością wyboru konkretnej metody podczas wykonywania. L. Stępień (AJD) POwCPP 4 / 32 Dziedziczenie Jedna klasa obiektów może być zdefiniowana jako szczególny przypadek innej ogólniejszej klasy, a definicje metod i pól danych klasy ogólniejszej umieszczane są automatycznie w klasie szczególnej. Klasa ogólna nazywana jest klasą bazową a klasa szczególna klasą pochodną. Klasy pochodne mogą definiować swoje własne metody i pola danych, które mogą przesłaniać dziedziczone metody i pola danych. Klasa może dziedziczyć właściwości więcej niż jednej klasy dziedziczenie wielorakie. L. Stępień (AJD) POwCPP 5 / 32 Programowanie zorientowane obiektowo Klasy Definicja klasy tworzy nowy typ danych. Klasy są opisami obiektów. Obiekty mogą przechowywać dane oraz wykonywać określone przez programistę zadania. Szkielet definicji klasy: class NazwaKlasy { // treść klasy }; Na treść klasy składają się definiowane pola i metody. Pola służą do przechowywania danych. Metody służą do wykonywania różnych operacji na danych. L. Stępień (AJD) POwCPP 6 / 32 Obiekty w C++ Obiekt: abstrakcyjny byt reprezentujący lub opisujący pewną rzecz lub pojęcie obserwowane w świecie rzeczywistym. Obiekt przechowuje pewne informacje na swój temat (atrybuty). Obiekt charakteryzuje się pewnym zakresem zachowań. Można poprosić obiekt o wykonanie pewnej operacji na samym sobie. Przykłady: Klasa zmienna; Klasa* wskaznik; wskaznik = new Klasa(argumenty); Klasa* wskaznik = new Klasa(argumenty); L. Stępień (AJD) POwCPP 7 / 32 Pola klas Pola są składowymi klasy, definiowanymi w jej wnętrzu (inaczej „w ciele klasy”). Po utworzeniu zmiennej typu klasowego (obiektu), do jej pól należy odwoływać się za pomocą operatora wyboru składowej oznaczanego znakiem kropki ’.’. zmienna.pole Po utworzeniu obiektu przy pomocy operatora new, do pól obiektu należy odwoływać się za pomocą operatora wyboru składowej, oznaczanego symbolem ’->’. wskaznik->pole L. Stępień (AJD) POwCPP 8 / 32 Metody klas Metody: Operacje wykonywane na obiektach. Są wykonywane na skutek wysłania do obiektu komunikatu, który wywołuje określoną metodę (operację). Metody noszą również nazwę funkcji składowych. Metody definiowane są w ciele klasy. Każda metoda może przyjmować argumenty oraz zwracać wynik. Argumenty metody to dane, które można jej przekazać. Metoda może mieć dowolną liczbę argumentów (w szczególności 0) umieszczonych w nawiasie okrągłym za jej nazwą, oddzielonych przecinkami. Metoda, która nie ma argumentów, ma puste nawiasy okrągłe. Metoda może zwracać wynik przez zastosowanie instrukcji return. Jeżeli metoda nic nie zwraca, jako zwracany typ należy zastosować słowo void. L. Stępień (AJD) POwCPP 9 / 32 Odwołanie do metod klasy Po utworzeniu obiektu do jego metod należy odwoływać się: za pomocą operatora wyboru składowej kropiki ’.’: Klasa zmienna; zmienna.metoda(argumenty); za pomocą operatora wyboru składowej oznaczonego symbolem ’->’, o ile obiekt został utworzony przy użyciu operatora new: Klasa* wskaznik = new Klasa(argumenty); wskaznik->metoda(argumenty); L. Stępień (AJD) POwCPP 10 / 32 Modyfikatory dostępu W języku C++ dostęp do składowych klasy jest określany za pomocą słów kluczowych (modyfikatorów dostepu): private: składowe nie są dostępne dla klienta klasy (aplikacji korzystającej z klasy), dostęp do tych składowych mają tylko metody klasy, public: składowe są dostępne dla klienta klasy, nie ma ograniczeń dostępu, protected: wykorzystywane podczas dziedziczenia, dostęp do nich mają metody klasy lub metody klas potomnych. Uwaga Domyślnie, jeżeli przed pierwszą składową klasy nie występuje żadne okreslenie, dostęp jest prywatny, co oznacza, że dostęp do tej składowej mają tylko metody definiowanej klasy. L. Stępień (AJD) POwCPP 11 / 32 Przykład 1 - definicja klasy class Baton { public: //metody void setNazwa(string nazwaBatonu) { nazwa = nazwaBatonu; } void setIlosc(int iloscBatonu) { ilosc = iloscBatonu; } void setCena(float cenaBatonu) { cena = cenaBatonu; } void wypisz() { cout << nazwa << ", " << ilosc << " szt., "; cout << cena << "zl" << endl; } private://pola string nazwa; int ilosc; float cena; }; L. Stępień (AJD) POwCPP 12 / 32 Przykład 1 - obiekty klasy int main() { Baton baton1; baton1.setNazwa("Mars"); baton1.setIlosc(14); baton1.setCena(3.15); baton1.wypisz(); Baton baton2; baton2.setNazwa("Prince Polo"); baton2.setIlosc(100); baton2.setCena(1.95); baton2.wypisz(); } L. Stępień (AJD) POwCPP 13 / 32 Przykład 2 - deklaracja metod w definicji klasy class Baton { public: //metody void setNazwa(string nazwaBatonu); void setIlosc(int iloscBatonu); void setCena(float cenaBatonu); void wypisz(); private://pola string nazwa; int ilosc; float cena; }; L. Stępień (AJD) POwCPP 14 / 32 Przykład 2 - definicja metod klasy Baton void Baton::setNazwa(string nazwaBatonu) { nazwa = nazwaBatonu; } void Baton::setIlosc(int iloscBatonu) { ilosc = iloscBatonu; } void Baton::setCena(float cenaBatonu) { cena = cenaBatonu; } void Baton::wypisz() { cout << nazwa << ", " << ilosc << " szt., "; cout << cena << "zl" << endl; } L. Stępień (AJD) POwCPP 15 / 32 Przykład 3 - inicjalizacja pól w ciele klasy tylko w C++11 class Baton { public: //metody void setNazwa(const string& nazwaBatonu); void setIlosc(int iloscBatonu); void setCena(float cenaBatonu); void wypisz(); private://pola string nazwa = ""; int ilosc = 0; float cena = 0.00; }; int main(){ Baton baton1; baton1.wypisz(); } L. Stępień (AJD) POwCPP 16 / 32 Przykład 4 - Baton.h #ifndef BATON_H #define BATON_H #include <string> using namespace std; class Baton { public: //metody void setNazwa(const string& nazwaBatonu); void setIlosc(int iloscBatonu); void setCena(float cenaBatonu); void wypisz(); private://pola string nazwa; int ilosc; float cena; }; #endif L. Stępień (AJD) POwCPP 17 / 32 Przykład 4 - Baton.cpp #include <iostream> #include "Baton.h" void Baton::setNazwa(string nazwaBatonu) { nazwa = nazwaBatonu; } void Baton::setIlosc(int iloscBatonu) { ilosc = iloscBatonu; } void Baton::setCena(float cenaBatonu) { cena = cenaBatonu; } void Baton::wypisz() { cout << nazwa << ", " << ilosc << " szt., "; cout << cena << "zl" << endl; } L. Stępień (AJD) POwCPP 18 / 32 Przykład 4 - main.cpp #include <iostream> #include <string> #include "Baton.h" using namespace std; int main() { Baton baton1; baton1.setNazwa("Mars"); baton1.setIlosc(14); baton1.setCena(3.15); baton1.wypisz(); } L. Stępień (AJD) POwCPP 19 / 32 Konstruktor klasy Konstruktor gwarantuje poprawną inicjalizację obiektu. Jest on automatycznie wywoływany przez kompilator, w miejscu, w którym tworzony jest obiekt, zanim jeszcze klient klasy będzie mógł podjąć jakiekolwiek działania związane z obiektem. Konstruktor może posiadać argumenty określające sposób tworzenia obiektu. L. Stępień (AJD) POwCPP 20 / 32 Konstruktory klas Konstruktor to specjalna metoda, która jest wywoływana podczas tworzenia obiektu. Musi mieć nazwę zgodną z nazwą klasy. Konstruktor nigdy nie zwraca żadnego wyniku, ale nie występuje przed nim słowo void. Konstruktor może być bezargumentowy, jak też może przyjmować argumenty, które zostaną wykorzystane, bezpośrednio lub pośrednio, np. do zainicjalizowania pól obiektu. Każda klasa może mieć kilka konstruktorów, różniących się przyjmowanymi argumentami - przeciążanie konstruktora. Jeśli w klasie występuje tylko jeden konstruktor i przyjmuje on argumenty, to przy tworzeniu obiektu należy je podać. L. Stępień (AJD) POwCPP 21 / 32 Konstruktory klasy Jeśli nie zdefiniuje się żadnego własnego konstruktora inicjującego, to kompilator automatycznie stworzy własny domyślny (czyli bez parametrów) konstruktor inicjujący. Konstruktor domyślny stworzony przez kompilator nie przypisuje wartości początkowych składowym klasy. Konstruktor domyślny to konstruktor, który może zostać wywołany bez podawania argumentów. Zdefiniowanie choć jednego własnego konstruktora spowoduje, że kompilator przyjmie, że klasa ma własne konstruktory i nie utworzy swojego konstruktora domyślnego. Jeśli potrzebny jest konstruktor bez parametrów, trzeba go samemu utworzyć. W ciele konstruktora (tak jak w każdej innej metodzie) można wywoływać inne metody. Podobnie jak w funkcjach i metodach, także w konstruktorach można zadeklarować wartości domyślne argumentów. L. Stępień (AJD) POwCPP 22 / 32 Przykład 5 - Baton.h #ifndef BATON_H #define BATON_H #include <string> using namespace std; class Baton { public: //metody Baton(const string& nazwaBatonu); void setNazwa(const string& nazwaBatonu); void setIlosc(int iloscBatonu); void setCena(float cenaBatonu); void wypisz(); private://pola string nazwa; int ilosc = 0; float cena = 0; }; #endif L. Stępień (AJD) POwCPP 23 / 32 Przykład 5 - Baton.cpp #include <iostream> #include "Baton.h" Baton::Baton(const string& nazwaBatonu) { nazwa = nazwaBatonu; } ... L. Stępień (AJD) POwCPP 24 / 32 Przykład 5 - main.cpp #include <iostream> #include <string> #include "Baton.h" using namespace std; int main() { Baton baton1("Mars"); baton1.setIlosc(14); baton1.setCena(3.15); baton1.wypisz(); Baton baton2 = Baton("Mars1"); baton2.setIlosc(4); baton2.setCena(2.15); baton2.wypisz(); Baton* baton3 = new Baton("Mars2"); baton3->wypisz(); } L. Stępień (AJD) POwCPP 25 / 32 this Wywołana funkcja składowa klasy (metoda) otrzymuje niejawnie adres obiektu, na rzecz którego została wywołana. Adres ten jest przechowywany w zmiennej wskaźnikowej o nazwie this. („Który obiekt? Ten (this) obiekt.”) Odwołanie do pól i metod obiektu do którego this się odwołuje, odbywa się za pomocą operatora wyboru składowej oznaczanego symbolem ’->’. Umożliwia to m.in. stosowanie w metodach i konstruktorach argumentów o nazwach identycznych z nazwami pól klasy. L. Stępień (AJD) POwCPP 26 / 32 Przykład 5 - Baton.h #ifndef BATON_H #define BATON_H #include <string> using namespace std; class Baton { public: //metody Baton(const string& nazwa=""); Baton(const string& nazwa, int ilosc, float cena); ... private://pola string nazwa; int ilosc = 0; float cena = 0; }; #endif L. Stępień (AJD) POwCPP 27 / 32 Przykład 5 - Baton.cpp #include <iostream> #include "Baton.h" Baton::Baton(const string& nazwa) { this->nazwa = nazwa; } Baton::Baton(const string& nazwa, int ilosc, float cena) { this->nazwa = nazwa; this->ilosc = ilosc; this->cena = cena; } ... L. Stępień (AJD) POwCPP 28 / 32 Metody stałe Metody, które nie zmieniają wartości pól obiektu mogą, a wręcz powinny być deklarowane jako const. class Baton { public: //metody ... void wypisz() const; ... }; void Baton::wypisz() const { cout << nazwa << ", " << ilosc << " szt., "; cout << cena << "zl" << endl; } L. Stępień (AJD) POwCPP 29 / 32 Zalecanym sposobem inicjalizacji pól obiektu jest zastosowanie listy inicjalizacyjnej. Lista inicjalizacyjna zaczyna się dwukropkiem umieszczonym za nawiasami zawierającymi argumenty. Po dwukropku występuje lista argumentów oddzielonych przecinkami, a po każdym argumencie podaje się w nawiasach wartości początkowe argumentów. class Baton { public: ... Baton(const string& nazwa, int ilosc = 0, float cena = 0.0); ... }; Baton::Baton(const string& nazwa, int ilosc, float cena) :nazwa(nazwa),ilosc(ilosc),cena(cena){} L. Stępień (AJD) POwCPP 30 / 32 Destruktory klas Destruktor jest to specjalna metoda automatycznie wywoływana podczas usuwania obiektu danej klasy, co ma miejsce, gdy: kończy się zasięg deklaracji obiektu, usuwany jest obiekt tymczasowy (patrz wykład 3), do wskaźnika obiektu zastosowano operator delete. Celem destruktora jest zakończenie istnienia obiektu danej klasy w sposób przewidywalny i uporządkowany. Jeśli nie zdefiniuje się własnego destruktora, to kompilator automatycznie stworzy własny domyślny destruktor. Destruktor definiowany przez użytkownika ma taką samą nazwę jak klasa poprzedzoną znakiem tyldy (∼), jest funkcją bez określonego typu wyniku. Do destruktora nie przekazujemy żadnych argumentów. W klasie można zdefiniować tylko jeden destruktor. L. Stępień (AJD) POwCPP 31 / 32 Przykład 6 - Destruktor class Baton { public: Baton(const string& nazwa, int ilosc = 0, float cena = 0.0); ~Baton(); ... }; Baton::~Baton() { cout << "Destruktor" << endl; } L. Stępień (AJD) POwCPP 32 / 32