• Nie Znaleziono Wyników

Wstęp do programowania obiektowego

N/A
N/A
Protected

Academic year: 2021

Share "Wstęp do programowania obiektowego"

Copied!
38
0
0

Pełen tekst

(1)

Wstęp do programowania obiektowego

WYKŁAD 3

Dziedziczenie

Pola i funkcje statyczne

Funkcje zaprzyjaźnione, this

(2)

Nazwa typu Rozmiar Zakres Uwagi bool 1 bit wartości true albo false stdbool.h

TYPY ZNAKOWE

char 1 bajt 0..255 (unsigned) -128.. 127 (signed)

reprezentuje znak w kodzie ASCII (7bit) + strona kodowa char16_t

char32_t

2 bajty 4 bajty

1 znak Unicode UTF-16

1 znak Unicode UTF-32 reprezentuje znak Unicode

TYPY CAŁKOWITE

int 2 lub 4 bajty zależnie od rozmiaru, taki

jak: short lub long int rozmiar zależy od kompilatora short int 2 bajty -32768.. 32767 (signed)

0..65535 (unsigned)

long int 4 bajty -2147483648..2147483647 (s) 0..4294967295 (unsigned)

long long int 8 bajtów

-9223372036854775808..

9223372036854775807 (s) 0..18446744073709551615 (us)

TYPY ZMIENNOPRZECINKOWE

float 4 bajty 3.4e +/- 38

(7 cyfr dziesiętnych) double 8 bajtów 1.7e +/- 308

(15 cyfr dziesiętnych) long double 8, 10 lub 12B 1.7e +/- 308

(15 cyfr dziesiętnych)

void reprezentuje brak typu

2

(3)

Napisowe typy danych w C/C++

char[] – (tzw. c-string) tablica znaków char,

(czyli znaki ASCII plus strona kodowa), obecny w C/C++.

string – typ obiektowy zawarty w klasie string, reprezentujący napis oraz użyteczne operacje na nim (rozmiar, łączenie, wycinanie,

porównanie, kopiowanie, początek, koniec, etc.) Wyłącznie C++.

(4)

Operowanie na C-stringach

#include <string.h> // strcpy()

#include <stdlib.h> // malloc()

#include <iostream>

using namespace std;

main()

{ char CC[7]; // C-string (6 znaków + NULL) char *CC2; // C-string (tylko wskaźnik) strcpy(CC,"Napis");

CC2 = (char *) malloc(15); // Alokacja pamięci strcpy(CC2, "To jest napis.");

cout << CC << endl;

cout << CC2 << endl;

}

4

(5)

Operowanie na obiektach klasy string

#include <string>

#include <iostream>

using namespace std;

main()

{ string SS; // C++ STL string string SS2; // C++ STL string SS = "This is a string";

SS2 = SS;

cout << SS << endl;

cout << SS2 << endl;

}

(6)

Definicja funkcji

<typ zwracany> <nazwa> (<lista parametrów>)

{

}

6

Deklaracja funkcji – nagłówek zakończony średnikiem

Sygnatura – lista typów parametrów z

uwzględnieniem kolejności. Przykłady sygnatur: (int) (int, int) (int, float) (float, int) () (char, float, int, int)

(7)

DZIEDZICZENIE

(8)

Dziedziczenie - definicja

Dziedziczenie - rozszerzanie funkcjonalności klasy bazowej i

stworzenie nowej klasy (dziedziczącej) na bazie tej klasy.

8

(9)

Schemat dziedziczenia w UML

Telewizor

# głośność

# kontrast

# jasność

+ getGlosnosc()

+ setGlosność(double) ...

TelewizorKolorowy - nasycenie

+ getNasycenie()

+ setNasycenie(double)

(10)

Zapis dziedziczenia w kodzie

class TVKolorowy : public TV {

...

}

10

(11)

Zapis dziedziczenia w kodzie

class TVKolorowy : public TV {

...

}

(12)

W C++ w klasie pochodnej możemy m.in.:

zdefiniować dodatkowe zmienne składowe (w naszym przykładzie: nasycenie)

zdefiniować dodatkowe metody (w naszym

przykładzie getNasycenie() i setNasycenie() )

zmieniać (nadpisywać) treść metod (zdefiniowanych jako „wirtualne”)

Konstruktory i destruktory nie są dziedziczone każda klasa ma własne! (ale podczas tworzenia każdego obiektu wywołuje się też konstruktor klas bazowej)

12

(13)

Korzyści z dziedziczenia:

oszczędność implementacji - nie musimy powtórnie definiować tej samej funkcjonalności

hierarchiczne uporządkowanie klas

tzw. polimorfizm – wywoływanie takich samych metod dla różnych klas (o

wspólnej klasie bazowej), daje różne efekty

Przykład: wywołujemy metodę

poruszajSię() dla Orła i Słonia, Orzeł

„lata”, Słoń „biega”

(14)

Dziedziczenie może być „wielopoziomowe”:

Zwierzę

Ssak Ptak Płaz

Słoń Żyrafa Orzeł Wrona Żaba

14

(15)

Dziedziczenie możemy też zobrazować jako zbiory i podzbiory obiektów:

Zwierzę Ssak

Słoń

Żyrafa

Ptak Orzeł

Wrona

Płaz

Żaba

(16)

Typy dziedziczenia (w C++)

W C++ mamy dostępne trzy typy dziedziczenia:

publiczne

prywatne

chronione

Typ dziedziczenia jest specyfikowany w nagłówku klasy pochodnej i może

powodować zmiany widoczności odziedziczonych składowych

(wyłącznie w klasie pochodnej).

16

(17)

Typy dziedziczenia (w C++)

Dziedziczenie publiczne - TVKol :public TV

public -> public

protected -> protected

private -> nigdy się nie dziedziczą

Dziedziczenie prywatne - TVKol :private TV

public -> private

protected -> private

private -> nigdy się nie dziedziczą

(18)

Dziedziczenie chronione

TVKol :protected TV

public -> protected

protected -> protected

private -> nigdy się nie dziedziczą

Najczęściej używane jest dziedziczenie publiczne, we współczesnych językach zwykle dostępne jest tylko ono.

18

(19)

Zmienne składowe i metody oznaczone jako protected, są

dziedziczone i dostępne w klasach pochodnych. Nie są natomiast

dostępne „na zewnątrz” struktury dziedziczenia.

(20)

DZIEDZICZENIE WIELOKROTNE

20

(21)

Dziedziczenie wielokrotne

Dziedziczenie wielokrotne (ang. multiple inheritance) nazywane także

dziedziczeniem wielobazowym to operacja polegająca na dziedziczeniu po więcej niż jednej klasie bazowej.

Samochód

# liczbaKół + jedź(int) ...

Amfibia - kolor ...

Statek

+ wyporność + płyń() ...

(22)

Dziedziczenie wielokrotne w kodzie (C++)

class Samochód { protected:

int liczbaKół;

public:

void jedź(int bieg) { …}

};

class Statek { public:

float wyporność;

void płyń() { … } };

class Amfibia : public Samochód, public Statek { private:

int kolor;

};

22

(23)

Dziedziczenie wielokrotne stosowane jest m.in. w językach C++, Perl.

W innych językach programowania (np.

w Javie) zwykle dopuszczalne jest

wyłącznie dziedziczenie jednokrotne, zaś do uzyskania efektu, który w C++

osiąga się poprzez dziedziczenie wielokrotne używa się tzw.

interfejsów.

(24)

Zaleta dziedziczenia wielokrotnego:

(niekiedy) intuicyjność dziedziczenia

Wady dziedziczenia wielokrotnego:

niejednoznaczność działania dla bardziej skomplikowanych drzew,

problemy z łańcuchowym wywoływaniem konstruktorów,

przesłanianie zmiennych i metod,

trudności implementacyjne.

24

(25)

POLA I FUNKCJE

STATYCZNE

(26)

Pola statyczne

Niekiedy potrzebne są pola wspólne dla wszystkich obiektów klasy.

W C++ i wielu innych językach takie pola można zadeklarować przy pomocy słowa kluczowego static. Tak zadeklarowane

pole jest widoczne we wszystkich

obiektach klasy, ale w pamięci istnieje tylko jeden egzemplarz takiego pola.

Pola statyczne są jak gdyby zmiennymi globalnymi danej klasy i istnieją

niezależnie od tworzonych obiektów klasy.

26

(27)

Pola statyczne w C++

W C++ potrzebna jest definicja, deklaracja i zwykle inicjacja pola statycznego:

class Punkt { private:

int x;

int y;

public:

static int a; //definicja pola statycznego publicznego

}

int Punkt::a = 1; //deklaracja i inicjacja pola statycznego poza klasą

(28)

Odwołanie do publicznego pola statycznego jest możliwe przez nazwę kwalifikowaną (nazwa klasy z operatorem zakresu ::) lub przez operator . i ->.

Punkt::a = 2; //odwołanie do pola statycznego przez ::

Punkt p1;

p1.a =2; //odwołanie do pola statycznego przez . Punkt *wsk;

wsk = new Punkt;

wsk->a=10; //odwołanie do pola statycznego przez ->

delete wsk;

28

(29)

Pole statyczne prywatne może być poza klasą tylko zainicjowane

(jednokrotny dostęp przy deklaracji).

Poza tym wyjątkiem poza klasą nie ma do niego dostępu.

(30)

Inicjalizacja pól statycznych w C++:

Pola statyczne w C++ nie mogą być inicjowane przez konstruktor. Inicjuje się je poza klasą przy użyciu nazwy kwalifikowanej (nazwa klasy i

czterokropek).

30

(31)

Funkcje statyczne

Funkcje statyczne deklarowane z

atrybutem static, podobnie jak pola

statyczne związane są z klasą a nie z obiektami; czyli:

Można się do nich odwoływać nawet gdy nie istnieje żaden obiekt

Niestatyczne pola i metody klasy nie są widoczne wewnątrz funkcji statycznych (ponieważ są związane z konkretnymi obiektami)

Pola statyczne są widoczne wewnątrz funkcji statycznych

(32)

Funkcje statyczne służą:

zwykle do wykonywania operacji na polach statycznych

mogą służyć do zapisu operacji nie związanych z zawartością klasy (np.

operacje na plikach, zewnętrznych bazach danych itp.).

32

(33)

Przykład użycia funkcji statycznej

class Wektor3d { private:

double x, y, z;

public:

static int wymiar;

static void zmien_wymiar( int nowy_wymiar) { wymiar = nowy_wymiar;}

};

int Wektor3d :: wymiar; //pole statyczne musi być zadeklarowane int main(){

Wektor3d w1;

w1.zmien_wymiar( 2);

cout << "Wymiar wektora w1 = " << w1.wymiar << endl;

Wektor3d :: zmien_wymiar(3);

cout << "Nowy wymiar wektora w1 = " << w1.wymiar << endl;

}

(34)

FUNKCJE

ZAPRZYJAŹNIONE

34

(35)

Funkcje zaprzyjaźnione w C++

Funkcje zewnętrzne albo funkcje innej klasy mogą mieć dostęp do pól

prywatnych (lub chronionych) klasy jeżeli zostaną zadeklarowane z

atrybutem friend.

Są zdefiniowane poza klasą,

wewnątrz klasy deklarujemy ich istnienie i fakt przyjaźni.

(36)

Przykład funkcji zaprzyjaźnionej

class Wektor3d { private:

double x, y, z;

friend double dlugosc( Wektor3d w);

};

double dlugosc( Wektor3d w) {

return sqrt(w.x * w.x + w.y * w.y + w.z * w.z);

}

int main() { Wektor3d w;

cout << ", dlugosc = " << dlugosc(w);}

36

klasa

funkcja

(37)

Cechy funkcji

zaprzyjaźnionych

Funkcja zaprzyjaźniona nie jest składową klasy (nie można się do niej odwołać poprzez nazwę

kwalifikowaną).

Funkcja zaprzyjaźniona nie może być aktywowana na rzecz jakiegoś obiektu

Funkcja zaprzyjaźniona powinna mieć jako wynik albo jako argument obiekt klasy, z którą jest zaprzyjaźniona (inaczej przyjaźń nie ma sensu!)

Każda funkcja może być zaprzyjaźniona z wieloma klasami.

W praktyce, najczęściej jako funkcje zaprzyjaźnione

deklaruje się funkcje operatorowe (operatory to np. + - * / ).

Ponieważ funkcje zaprzyjaźnione są naruszaniem reguł enkapsulacji, powinno się eksponować jej

deklaracje, umieszczając je na samym początku deklaracji ustrojów klas.

Ponadto starajmy się nie nadużywać przyjaźni.

(38)

Słowo kluczowe this

Wskaźnik this, automatycznie dodawany przez kompilator do każdego obiektu, jest wskazaniem na ten właśnie obiekt. Funkcje składowe mogą w ten sposób zwrócić wskaźnik na obiekt:

return this;

lub też sam obiekt:

return *this;

Przykład - definicja operatora powiększania dla

liczb zespolonych:

Zespolone operator+= (const Zespolone& z) { re+=z.re; im+=z.im;

return *this;

}

38

Cytaty

Powiązane dokumenty

– osoba, która poprawia zaliczenie (końcowe lub cząstkowe), może uzyskać co najwyżej ocenę bardzo dobrą.

rok akademicki 2019/20 semestr zimowy..

 Przeszukiwany jest stos wywołań funkcji w poszukiwaniu takiej, która zawiera obsługę wyjątku danego typu (czyli odpowiednią instrukcję catch).. ◦ Jeżeli

• Parametrami szablonów mogą być również szablony klas, jako tak zwane szablony parametrów szablonów.. Stack&lt;int, std::vector&gt;

 OutputIterator set_union (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);.  OutputIterator

Model dziedziny (uzupełniony): klasy, atrybuty klas oraz

 Jednostka programu, która zadeklarowała instancję klasy (obiekt), ma dostęp do publicznych bytów tej klasy, ale tylko poprzez tę instancję.  Każda instancja klasy ma

 Symbole pojawiające się wyłącznie po prawej stronie to symbole terminalne.  Generalnie symbole terminalne to symbole z alfabetu definiowanego języka,