Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Programowanie obiektowe w C++
Paweł Jarzyński Artur Pacholec
2
Definicja programowania obiektowego
Programowanie obiektowe (nazywane też zorientowanym obiektowo) to paradygmat programowania w którym programy są definiowane za pomocą struktur łączących stan i zachowanie pewnych bytów przetwarzanych w trakcie działania programu. Stan jest reprezentowany za pomocą zmiennych natomiast zachowanie definiują metody. Struktury łączące stan i zachowanie nazywane są klasami i stanowią wzorzec na podstawie którego tworzone są unikalne byty zwane obiektami.
3
Historia obiektowych języków programowania
Koncepcja programowania obiektowego pojawiła się w języku Simula 67 i miała być rozwiązaniem problemu opanowania wszystkich zależności pomiędzy danymi i operacjami których pogrupowanie pomogło zapanować nad skomplikowanymi programami symulacyjnymi. Pierwszym w pełni obiektowym językiem programowania był Smalltalk który powstał w latach 70 i był wzorem dla języka C++ którego popularność spowodowała, że w latach 80 programowanie obiektowe stało się techniką dominującą aż do dnia dzisiejszego. W latach 90 pojawił się obiektowy język Java, a niedługo później Microsoft stworzył wzorowany na nim język C#.
4
Podstawowe cechy języków obiektowych
Abstrakcja pozwala tworzyć intuicyjny interfejs programistyczny umożliwiający odczyt i modyfikację stanu bytów (obiektów) bez bezpośredniego dostępu do danych które ten stan opisują. Hermetyzacja (zwana też czasem enkapsulacją) ogranicza bezpośredni dostęp do danych obiektu i podprogramów, które na nich operują, w celu uniemożliwienie wykonania niedozwolonych operacji. Dziedziczenie umożliwia definiowanie nowych typów (klas), w oparciu o te już wcześniej definiowanie, co tworzy model hierarchiczny zwany drzewem dziedziczenia. Polimorfizm to mechanizm dynamicznego wiązania właściwego zachowania z bytem (obiektem) utworzonym na podstawie konkretnego wzorca (klasy) w hierarchii dziedziczenia.
5
Pojęcie klasy i obiektu
Klasa (nazywana często typem) definiuje dane (zmienne) oraz zachowanie (podprogramy) bytów i stanowi wzorzec na podstawie którego powoływane są do życia konkretne ich egzemplarze zwane obiektami. Zmienne zdefiniowane w klasie nazywane są polami a podprogramy na nich operujące metodami klasy. Obiekt (nazywany często instancją klasy) jest unikalnym bytem skonstruowanym w oparciu o konkretną klasę, posiadającym stan reprezentowany przez unikalną dla siebie grupę danych jakie w sobie skupia. Obiekt jest dostępny poprzez zmienną lub wskaźnik/referencję która na niego wskazuje. Odczyt i modyfikacja jego stanu jest możliwa poprzez bezpośrednie odwołanie się do jego pól lub za pośrednictwem metod zdefiniowanych w klasie której dany obiekt jest egzemplarzem.
6
Definiowanie klasy W języku C++ można definiować klasy na dwa sposoby, różniące się miejscem implementacji metod: implementacja razem z definicją klasy rozdzielenie implementacji i definicji
7
Implementacja z definicją
class Matrix { private: int n, m; double **numbers; public: Matrix(int n, int m) { ... } ~Matrix() { double det() { double get(int i, int j) { return numbers[i][j]; };
8
Rozdzielona definicja i implementacja
Część nagłówkowa (plik *.h) Część implementacyjna (plik *.cpp) class Matrix { private: int n, m; double **numbers; public: Matrix(int, int); ~Matrix(); double det(); double get(int, int); }; Matrix::Matrix(int n, int m) { ... } Matrix::~Matrix() double Matrix::det() double Matrix::get(int i, int j) return numbers[i][j];
9
Zmienna this Zmienna this używana w ciele metod przechowuje zawsze adres obiektu na rzecz którego metoda będzie wywołana. class Matrix { private: int n, m; double **numbers; public: double get(int i, int j) return this->numbers[i][j]; } };
10
Pola i metody stałe Pola z modyfikatorem const są przeznaczone tylko do odczytu. Metody const nie mogą zmieniać stanu obiektu czyli nie mogą modyfikować pól ani wywoływać innych metod które nie są stałe. class Matrix { private: const int n, m; double **numbers; public: double get(int i, int j) const return this->numbers[i][j]; } };
11
Obiekty stałe Obiekty const służą tylko do odczytu ich stanu, bez możliwości jego zmiany, czyli nie można modyfikować ich pól, ani wywoływać metod które nie są stałe. const Matrix mcHammer; //can’t touch this Pola mutable mogą być modyfikowane przez metody stałe lub gdy stały jest cały obiekt. class A { private: int x, y; mutable int tmp; public: int exec() const // można modyfikować pole tmp } };
12
Pola statyczne Pola statyczne należą do klasy i nie są unikalne dla każdego z jej obiektów (czyli są globalne). class A { public: static int a; int b; }; Ponieważ pola statyczne należą do klasy, nie są alokowane wraz z jej obiektami i dlatego wymagają definicji poza klasą (ponieważ klasa zawiera tylko deklarację pól). int A::a; //w .cpp
13
Metody statyczne Metody statyczne nie mogą używać zmiennej this ponieważ nie są wywoływane na rzecz istniejącego obiektu. class A { public: static int f() ... } }; A::f(); A a; a.f();
14
Różnice między podejściem obiektowym i proceduralnym
struct Stack { Node* top; int size; }; void init(Stack* stack); void destroy(Stack* stack); void push(Stack* stack, int value); int pop(Stack* stack); class Stack { private: Node* top; int size; public: Stack(); ~Stack(); void push(int value); int pop(); };
15
Konstruktory i destruktory
Konstruktor jest specjalną metodą wywoływaną automatycznie podczas tworzenia nowego obiektu. Destruktor również jest specjalną metodą wywoływaną automatycznie podczas niszczenia obiektu.
16
class Matrix { private: int n, m; double
class Matrix { private: int n, m; double **numbers; public: Matrix(int n, int m) this->n = n; this->m = m; numbers = new double*[n]; for(int i = 0; i < n; ++i) numbers[i] = new double[m]; } ~Matrix() delete[] numbers[i]; delete[] numbers; };
17
Konstruktory i destruktory II
Klasa może posiadać wiele konstruktorów różniących się typami pobieranych parametrów lub ich ilością (domyślnie konstruktor jest bezargumentowy i nie trzeba go definiować). Klasa może posiadać dokładnie jeden destruktor który nie pobiera żadnych argumentów (nie trzeba go definiować jeśli nie ma takiej potrzeby). class Matrix { public: Matrix(int n); Matrix(int n, int m); Matrix(int n, int m, double value); ~Matrix(); };
18
Konstruktor kopiujący
Gdy nowo tworzony obiekt jest inicjalizowany już istniejącym lub gdy obiekt jest przekazany jako argument do funkcji czy metody, tworzona jest jego kopia. Domyślnie tworzona jest kopia nazywana płytką kopią, która przepisuje wartości wszystkich pól klasy. Może to nie wystarczyć gdy obiekty operują na złożonych danych, alokowanych dynamicznie, ponieważ wartościami pól są wtedy adresy, a nie dane które się pod nimi znajdują. Kopia całego obiektu wraz z dynamicznie zaalokowanymi danymi, z którymi jest powiązany nazywana jest kopią głęboką i wymaga utworzenia konstruktora kopiującego.
19
class Matrix { private: int n, m; double
class Matrix { private: int n, m; double **numbers; public: Matrix(int n, int m); Matrix(const Matrix& matrix) n = matrix.n; m = matrix.m; numbers = new double*[n]; for(int i = 0; i < n; ++i) { numbers[i] = new double[m]; for(int j = 0; j < m; ++j) numbers[i][j] = matrix.numbers[i][j]; } ~Matrix(); };
20
Lista inicjalizacyjna konstruktora
Lista inicjalizacyjna jest wygodnym sposobem na czytelne oddzielenie części konstruktora której zadaniem jest nadanie polom wartości, od tej która wykonuje bardziej złożone operacje na tych polach w celu przygotowania obiektu do stanu użyteczności. Matrix::Matrix(int n, int m): n(n), m(m) { numbers = new double*[n]; for(int i = 0; i < n; ++i) numbers[i] = new double[m]; }
21
Lista inicjalizacyjna konstruktora II
Jeśli klasa zawiera referencje, pola stałe albo takie które są obiektami innej klasy, nie posiadającej konstruktora bezargumentowego, to utworzenie takiej listy jest obowiązkowe. class A { private: const double x; double& y; Matrix matrix; public: A(const double valueX, double& valueY, int n, int m): x(valueX), y(valueY), matrix(n, m) { } }; POŁOWA!!
22
Abstrakcja danych Abstrakcja umożliwia opakowywanie danych oraz algorytmów które te dane przetwarzają w klasy i dostarczenie programiście pewnego interfejsu przy pomocy którego będzie mógł przetwarzać obiekty bez znajomości szczegółów implementacyjnych algorytmów które za pośrednictwem tego interfejsu uruchamia. list.last() list.sort() matrix.det() matrixA * matrixB
23
Hermetyzacja Hermetyzacja pozwala sterować dostępem do danych lub metod które nie powinny być przetwarzane "na zewnątrz" w sposób bezpośredni. Podstawowe modyfikatory dostępu jakie można spotkać w obiektowych językach programowania to public, protected oraz private. class A{ private: // składowe prywatne protected: // składowe chronione public: // składowe publiczne };
24
Akcesory i modyfikatory
Deklaracja pól (nawet tych które mają być dostępne do odczytu i modyfikacji) w sekcji publicznej niesie ze sobą ryzyko, że mogą one zostać zmodyfikowane "na zewnątrz" obiektu w niewłaściwy sposób który zaburzy działanie programu lub nawet uniemożliwi dalsze jego wykonywanie. Sposobem na wyeliminowanie tego ryzyka jest deklaracja wszystkich pól obiektu w sekcji prywatnej i utworzenie metod przeznaczonych do odczytu i modyfikacji wartości tych pól w sposób kontrolowany.
25
class A{ private: double x; public: double getX() const; void setX(double); }; inline double A::getX() const{ return x; } inline void A::setX(double value){ if(value >= 0.0 && value <= ) x = value;
26
Klasy i funkcje zaprzyjaźnione
Czasem zachodzi potrzeba utworzenia wyjątków od nałożonych przez hermetyzację barier i umożliwienia dostępu do wszystkich składowych konkretnej klasie lub funkcji. Aby naruszyć hermetyzację i umożliwić dostęp do wszystkich składowych niezależnie od sekcji w jakiej zostały zadeklarowane należy użyć deklaracji friend. class A{ private: double x[2], y[2]; friend double f(const A&); }; double f(const A& a){ return a.x[0] * a.y[0] + a.x[1] * a.y[1]; }
27
class Node{ private: int value; Node(int value): value(value){} friend class List; }; class List{ Node* head; Node* tail; int size; public: List(): head(NULL), tail(NULL), size(0){} ...
28
Kompozycja Kompozycją nazywamy strukturę danych w której polami jednej klasy są obiekty lub kolekcje obiektów innej klasy. class A{ ... }; class B{ class C{ A a; list<B> bList; class D{ C c1, c2;
29
class Punkt{ double x, y; }; class Bron{ int sila; ... class Postac{ Punkt pozycja; int poziomZdrowia; list<Bron*> bronie; class Budynek{ int wytrzymalosc; class Plansza{ list<Bron> dostepneBronie; list<Postac> postacie; list<Budynek> budynki;
30
Dziedziczenie Dziedziczenie pozwala na definiowane nowych klas w oparciu o już zdefiniowane rozszerzając tym samym ich funkcjonalność. Klasa która w swojej definicji dziedziczy po już zdefiniowanej otrzymuje wszystkie jej pola oraz dostęp do niektórych pól oraz metod. Dostęp do odziedziczonych składowych można dodatkowo ograniczyć przy pomocy znanych modyfikatorów.
31
class A{ private: int a; void f(){. } protected: int b; void g(){
class A{ private: int a; void f(){...} protected: int b; void g(){...} public: int c; void h(){...} }; class B: public A{ public: int d, e; void h(){ A::h(); ... } void f2(){...} };
32
class Punkt{ double x, y; }; class Tekstura{
class Punkt{ double x, y; }; class Tekstura{ ... class Obiekt{ Punkt pozycja; Tekstura tekstura; class ObiektNieruchomy: public Obiekt{ class Budynek: public ObiektNieruchomy{ class Roslina: public ObiektNieruchomy{ class ObiektRuchomy: public Obiekt{ int predkoscMaksymalna; ... }; class Pojazd: public ObiektRuchomy{ int poziomPaliwa; class Postac: public ObiektRuchomy{ int poziomZdrowia; list<Born*> bronie;
33
Wielodziedziczenie Niektóre języki (w tym C++) pozwalają na dziedziczenie po kilku klasach jednocześnie co nazywa się wielodziedziczeniem. class A{ public: int x; }; class B{ double x; class C: public A, public B{ ... C c; c.A::x; c.B::x;
34
Dziedziczenie a konstruktor
Jeśli nadklasa nie posiada konstruktora bezargumentowego lub zajdzie potrzeba wywołania jej konstruktora z pewnymi argumentami to w definicji konstruktora klasy pochodnej należy użyć listy inicjalizacyjnej. class A{ protected: int x, y; public: A(int x, int y): x(x), y(y){} }; class B: public A{ ... B(int x, int y): A(x, y){}
35
Kolejność wywoływania a dziedziczenie
Konstruktory są wywoływane począwszy od tego z klasy bazowej a skończywszy na konstruktorze klasy której obiekt jest tworzony. Podczas niszczenia obiektu destruktory są wywoływane w dokładnie odwrotnej kolejności.
36
Polimorfizm Polimorfizm jest mechanizmem dynamicznego wiązania wywołań metod z właściwym dla danego typu obiektu kodem tych metod. W języku C++ należy jawnie zadeklarować takie zachowanie dla metody o konkretnej nazwie za pomocą słowa kluczowego virtual. class A{ public: virtual void f(){...} }; class B: public A{ void f(){...} class C: public B{
37
Polimorfizm II Należy zauważyć, że możliwe jest przypisanie do wskaźnika typu A adresu obiektów jest podklasy B lub C. A* o1 = new A; o1->f(); A* o2 = new B; o2->f(); A* o3 = new C; o3->f(); Ta sama własność dotyczy również referencji. C c; A& o4 = c; o4.f();
38
class Obiekt{ Punkt pozycja; virtual bool przemiesc(Punkt nowaPozycja){ pozycja = nowaPozycja; return true; } }; class Pojazd: public Obiekt{ double spalanie; double poziomPaliwa; bool przemiesc(Punkt nowaPozycja){ if(poziomPaliwa < spalanie * odleglosc(pozycja, nowaPozycja)) return false; Obiekt::przemiesc(nowaPozycja); poziomPaliwa -= spalanie * odleglosc(pozycja, nowaPozycja); class Postac: public Obiekt{ int energia; list<Bron*> bronie; ... bool Plansza::zmienPozycje(Obiekt& obiekt, Punkt nowaPozycja){ if(pozycjaWolna(nowaPozycja)) return obiekt.przemiesc(nowaPozycja);
39
Klasy abstrakcyjne Metoda abstrakcyjna w danej klasie to taka której implementacja nie została jeszcze zdefiniowana (klasa zawiera tylko jej prototyp), ale wiadomo, że nastąpi to w co najmniej jednej z klas pochodnych. Klasa która zawiera przynajmniej jedną metodę abstrakcyjną (zadeklarowaną w taj klasie lub w jednej z klas po których dziedziczy, ale do tej pory nie zaimplementowaną) nazywana jest klasą abstrakcyjną i nie można tworzyć jej obiektów. Takie rozwiązanie ma sens jeśli metoda abstrakcyjna będzie również wirtualna bo wtedy można się nią posługiwać stosując wskaźniki albo referencje do klas które nie zawierają jeszcze jej implementacji dlatego każda metoda abstrakcyjna musi być również wirtualną. Ponieważ nie można tworzyć obiektów klas abstrakcyjnych to mamy pewność, że taki wskaźnik lub referencja będzie wskazywała na obiekt klasy w której wszystkie metody abstrakcyjne zostały odpowiednio zdefiniowane.
40
class Abstrakcyjna{ public: virtual void przetwarzaj() = 0; }; class A: public Abstrakcyjna{ void przetwarzaj(){ // przetwarzanie A } class B: public Abstrakcyjna{ // przetwarzanie B class C: public B{ // przetwarzanie C void przetwarzajObiekt(Abstrakcyjna& obiekt){ obiekt.przetwarzaj();
41
class Szyfr{ protected: virtual void szyfruj(const char
class Szyfr{ protected: virtual void szyfruj(const char* tekstJawny, char* szyfrogram, int rozmiar) = 0; public: void szyfrujPlik(const char* nazwaPlikuWejsciowego, const char* nazwaPlikuWyjsciowego){ ifstream plikWejsciowy(nazwaPlikuWejsciowego, ifstream::binary); ofstream plikWyjsciowy(nazwaPlikuWyjsciowego, ofstream::binary); char buforTekstJawny[1024], buforSzyfrogram[1024]; while(true){ plikWejsciowy.read(buforTekstJawny, 1024); if(plikWejsciowy.gcount() == 0) break; int odczytano = plikWejsciowy.gcount(); szyfruj(buforTekstJawny, buforSzyfrogram, odczytano); plikWyjsciowy.write(buforSzyfrogram, odczytano); } plikWejsciowy.close(); plikWyjsciowy.close(); }; class SzyfrPodstawieniowy: public Szyfr{ private: void szyfruj(const char* tekstJawny, char* szyfrogram, int rozmiar){ ... class SzyfrStrumieniowy: public Szyfr{
42
Interfejsy Interfejsem nazywamy pewien zbiór metod które dana klasa musi posiadać aby mogła realizować z góry ustaloną funkcjonalność. W języku C++ interfejs utożsamia się z klasą abstrakcyjną która zawiera wyłącznie metody abstrakcyjne a do jego implementacji często wykorzystuje się wielodziedziczenie którego nie ma w językach Java, C# i PHP dlatego interfejsy są tam odrębnym elementem.
43
class BazaDanych{ public: virtual bool polacz() = 0; virtual bool dodaj(const Dane&) = 0; virtual Dane pobierz(string) = 0; virtual void rozlacz() = 0; }; class System{ private: BazaDanych& baza; ... System(BazaDanych& baza): baza(baza){ } class BazaSQL: public BazaDanych{ bool polacz(){...}; bool dodaj(const Dane& dane){...}; Dane pobierz(string zapytanie){...}; void rozlacz(){...};
44
Szablony Szablony (zwane inaczej polimorfizmem statycznym) pozwalają na pisanie kodu programu który operuje na danych bez podawania konkretnych typów tych danych co nazywa się programowaniem uogólnionym. template <typename T> class Matrix{ private: int n, m; T** numbers; public: Matrix(int, int); ~Matrix(); T det(); T get(int, int); void set(int, int, T); }; Matrix<float> a(3, 3); Matrix<double> b(3, 3); Matrix<long double> c(3, 3);
45
template <typename T> class Node{ private: T value; public: Node(T value): value(value){} T get() const{ return value; } void set(T value){ this->value = value; }; class Stack{ Node<T>* data; Stack(): data(NULL){} ~Stack(){...} T top(){...} void push(T value){...} void pop(){...} Stack<int> s; Stack<Matrix> s;
46
Przestrzenie nazw Przestrzenie nazw służą eliminacji potencjalnych konfliktów nazw identyfikatorów. namespace Moja{ class A{...}; class B{...}; } namespace Inna{ Moja::A aMoja; Inna::B bInna; using Moja::A; A aMoja; Inna::A aInna; using namespace Moja; B bMoja;
47
Wyjątki Mechanizm wyjątków pozwala przekazać sterowanie do odpowiedniego miejsca gdy zaistnieje sytuacja uniemożliwiająca dalsze wykonywanie programu. Matrix operator*(const Matrix& a, const Matrix& b){ if(a.m != b.n) throw domain_error("wymiary macierzy niezgodne"); Macierz c; // mnożenie macierzy return c; } try{ ... Matrix c = a * b; }catch(domain_error& e){ // obsługa wyjątku
48
Pytania? Przemyślenia? Chęć pójścia do domu?
Dziękujemy za uwagę Pytania? Przemyślenia? Chęć pójścia do domu?
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.