PO wyk05 v1

background image

Klasy

Klasy

stanowią

rozszerzenie

możliwości

C++,

pozwalające

na

reprezentowanie i rozwiązywanie złożonych, rzeczywistych problemów.

Tworzenie nowych typów

Poznaliśmy już różne rodzaje typów zmiennych. Typ zmiennej zawiera
informację o rodzaju wartości przechowywanych przez tę zmienną. Jeżeli
zdefiniujecie zmienną Liczba jako unsigned short, to wiadomo będzie, że
zmienna ta będzie mogła przyjmować wartości od 0 do 65535 (przy
założeniu, że unsigned short zajmuje dwa bajty).

W praktyce, to właśnie oznacza, że zmienna jest typu unsigned short. Nie
chodzi tu o to, że zakres wartości jest "produktem ubocznym" bycia zmienną
typu unsigned short.
Oprócz informacji o rozmiarze, typ zawiera również dane o możliwościach i
właściwościach obiektu. Np. liczby typu short mogą być dodawane. Oznacza
to, że sama deklaracja zmiennych Liczba1 i Liczba2 jako short mówi, że
liczby te będzie można dodać, a wartość przypisać innej zmiennej.

 Rozmiarze w pamięci

 Rodzaju przechowywanej informacji

 Możliwych do wykonania czynnościach

Typ zawiera informacje o:

background image

Co to jest typ?

Typ można porównać do kategorii.

Jedną z umiejętności wyróżniających człowieka jest przypisywanie rzeczy i
zjawisk do określonych kategorii.

W lesie nie widzimy tysiąca kształtów, widzimy zwierzęta i drzewa. Również
zwierzęta możemy podzielić: zające, wilki, jelenie itp.

Przykład:

W C++ typ to obiekt z określonym rozmiarem, zbiorem możliwych do
wykonania operacji i stanem. Programista C++ może stworzyć dowolny
typ. Każdy z tych typów może mieć taką samą funkcjonalność jak
standardowe typy.

Tworzymy klasyfikacje, porządek, grupy, podziały i klasy. W skrócie można
powiedzieć, że tworzymy różne typy rzeczy.

Pomarańcza

Cytrus

Owoc

Roślina

Żywa rzecz

background image

W jakim celu tworzymy nowe typy?

Zazwyczaj programy piszemy po to, aby rozwiązywać rzeczywiste, istniejące
problemy takie jak obsługa bazy danych o zatrudnionych czy symulacja pracy
systemu centralnego ogrzewania.

Mimo, że każdy z tych
problemów można rozwiązać
posługując

się

jedynie

liczbami

całkowitymi

i

znakami,

to

nieporównywalnie szybciej i
łatwiej możemy otrzymać
wynik

dla

złożonego

problemu jeśli stworzymy
reprezentacje obiektów, o
których mówimy, i które
analizujemy.

Innymi

słowy,

jeśli

symulujemy

działanie

systemu

centralnego

ogrzewania

, to wygodnie

jest

stworzyć

zmienne

reprezentujące

pokoje

,

termostaty

i

termy

. Im

bardziej zmienne (model)
przystają

do

rzeczywistości, tym łatwiej
jest napisać program, który
rozwiąże dany problem.

background image

Klasy i ich zmienne

Nowy typ tworzy się poprzez zadeklarowanie klasy.

Klasa to zbiór zmiennych połączonych ze zbiorem odpowiadających im

funkcji.

Przykład:

sposoby reprezentacji

samochodu

polega na wymienieniu jego
składników:

 drzwi,
 koła,
 okna,

siedzenia,
 itp..

informacje o możliwościach
samochodu:

może się poruszać,

 przyspieszać,
 zwalniać
 itp...

I

II

background image

Można prowadzić samochód bez znajomości budowy silnika. Podobnie można
wykorzystywać klasy.

Hermetyzacja

polega na łączeniu wszystkich informacji, możliwości,

zalet jednostki w jeden obiekt.

Zalety hermetyzacji wszystkich informacji o samochodzie:

 wszystko jest zgromadzone w jednym miejscu

 można łatwo dane reprezentować, kopiować i nimi manipulować.

Klienci waszej klasy to inne klasy lub funkcje wykorzystujące przez was
napisaną klasę. Hermetyzacja pozwala na wykorzystanie obiektu bez
znajomości jego wewnętrznych mechanizmów i sposobu działania.

Przykład

Jedyna rzecz, którą musicie wiedzieć, to co dana klasa robi, a nie jak
ona to robi.

background image

Metody klasy to funkcje w danej klasie. Stanowią one taką samą część
klasy jak zmienne wewnętrzne. Metody decydują o możliwościach danej
klasy.

Każda klasa może się składać z dowolnie złożonego zbioru zmiennych i
innych klas. Zmienne w klasie określane są jako wewnętrzne zmienne klasy
lub jako wewnętrzne dane klasy.

Przykład:

Klasa samochód ma wewnętrzne zmienne reprezentujące siedzenia, radio,
koła itp.

Wewnętrzne zmienne klasy, określane również jako wewnętrzne dane klasy,
stanowią zmienne danej klasy.

Są jej częścią, podobnie jak koła, silnik itp. są częścią samochodu.

Funkcje w klasie zazwyczaj operują na wewnętrznych zmiennych klasy.
Określane są one jako wewnętrzne funkcje klasy lub częściej jako metody
danej klasy.

Metody klasy Samochód to np.: UruchomSilnik () czy Zahamuj () .

Klasa Kot może mieć zmienne wewnętrzne takie jak wiek i waga. Metody
to np.: Zasnij () , Miaucz () , LapMyszy () .

background image

Deklarowanie klasy

Do deklarowania klasy służy słowo kluczowe class. Po nim podajemy nazwę
tworzonej klasy, a następnie w klamrach zmienne wewnętrzne i metody.
Deklarację kończy się średnikiem.

Przykład deklaracji klasy o nazwie Kot:

class Kot
{
public:
unsigned int nWiek;
unsigned int nWaga;

Miauczy() ;
};

Deklaracja tej klasy nie rezerwuje pamięci na nią. Mówi ona kompilatorowi co
to jest Kot, jakie dane zawiera (nWiek, nWaga) i co potrafi robić ( Miauczy () ).

Dodatkowo, deklaracja niesie ze sobą informacje o rozmiarze Kota, tzn. ile
miejsca należy zarezerwować na każdego stworzonego Kota.

W tym przypadku (jeśli int zajmuje
2 bajty) to każdy Kot będzie
zajmował 4 bajty: nWiek zajmuje 2
bajty, podobnie nWaga.

Miauczy () nie zajmuje miejsca
ponieważ dla metod klasy nie
rezerwuje się obszaru w pamięci.

background image

Definiowanie obiektu

Obiekt nowego typu definiuje się tak, jak np. zmienną typu int:

unsigned int nMasa; // definicja unsigned int
Kot oFilemon; // definicja Kota

Klasy a obiekty

Kiedy posiadamy kota, to nie jest to jego definicja, lecz konkretne zwierzę.
Musicie odróżniać ogólną definicję od zwierzątka, aktualnie strącającego
wazon w waszym pokoju.

Podobnie w C++ odróżniamy definicję klasy Kot, która jest jedynie opisem
kota od konkretnego obiektu typu Kot. oFilemon jest obiektem typu Kot,
dokładnie tak samo jak nMasa jest zmienną typu unsigned int.
Obiekt to pojedyncze, indywidualne wystąpienie klasy.

background image

Dostęp do zasobów klasy

Jeśli macie zdefiniowany obiekt Kot (np. oFilemon) to dostęp do jego

zmiennych wewnętrznych i funkcji odbywa się za pomocą operatora kropki ( .

).

Przykład

Jeżeli chcecie nadać zmiennej wewnętrznej nJegoWaga wartość 50 to trzeba
napisać:

oFilemon.nJegoWaga = 50;

Podobnie wywołuje się funkcje wewnętrzne:

oFilemon.Miaucz() ;

Przypisywanie do obiektów

W C++ nie przypisuje się wartości do typów, tylko do zmiennych. W żadnym
wypadku nie można napisać tak:

int = 5 ;

Kompilator

zwróci

błąd

, ponieważ nie

można przypisać 5 do
typu całkowitego (int).

background image

Zamiast tego, trzeba zdefiniować zmienną typu int i dopiero jej nadać
wartość 5.

int x ; //definicja x jako int
x = 5 ; //przypisanie wartości 5 do x

Można powiedzieć, że zapis ten jest skrótem rozkazu "Przypisz 5 do zmiennej
x, która jest typu int". Podobnie jak poprzednio również w przypadku klasy
nie można napisać tak:

Najpierw trzeba zdefiniować obiekt typu Kot i dopiero wtedy można przypisać
5 do odpowiedniej zmiennej wewnętrznej.

Kot.nWiek = 5 ;

Kot oFilemon;
oFilemon.nWiek = 5;

Przykład

Przykład

background image

Prywatne kontra publiczne

Przy deklaracji klas używa się różnych słów kluczowych. Jednymi z
ważniejszych są:

Wszystkie elementy klasy -
dane i metody - są domyślnie
traktowane jako prywatne.

private

Oznacza to, że dostęp do nich
może być realizowany tylko
poprzez metody danej klasy.

public

Do elementów publicznych ma
się dostęp bezpośredni, we
wszystkich

obiektach

danej

klasy.

Przykład:

class Kot
{
unsigned int nWiek;
unsiqned int nWaga;

Miaucz () ;
};

W tej deklaracji, wszystkie trzy elementy
klasy: nWiek, nWaga i Miaucz() są
prywatne (ponieważ domyślnie przyjmuje
się, że jeśli nie określi się tego jawnie, to
elementy klasy są prywatne).

Kot oFilemon;
oFilemon.nWiek = 5;

Kompilator zgłosi komunikat błędu.
Próba dostępu do prywatnych danych

Krótko mówiąc, definicja klasy Kot mówi

kompilatorowi, że dostęp do jej elementów

może być realizowany tylko poprzez metody

klasy Kot.

background image

Uwaga: Aby mieć dostęp do elementów klasy Kot trzeba powiedzieć
kompilatorowi, które informacje ma traktować jako publiczne.

class Kot
{
public:
unsiqned int nWiek;
unsigned int nMasa;

Miaucz () ;
};

Teraz nWiek, nMasa i Miaucz() są
publiczne.

Linia:

oFilemon.nWiek = 5;

skompiluje się bez problemów.

Zawsze jednak musicie stworzyć funkcje publiczne (metody dostępu), dzięki
którym będziecie mogli wykonywać operacje na danych przechowywanych w
obiektach klasy.

Metody dostępu to publiczne funkcje danej klasy, dzięki którym inne
części programu będą mogły pobierać i ustawiać wartości zmiennych
wewnętrznych. Funkcje dostępu pozwalają na oddzielenie od siebie
problemu przechowywania danych od problemu ich wykorzystywania.
Pozwala to na zmianę sposobu przechowywania informacji bez
jakichkolwiek zmian funkcji, które z nich korzystają.

Dzięki słowom kluczowym private i public można dzielić deklarację
klasy na bloki publiczne i prywatne.

Starajcie się, żeby elementy danej klasy były prywatne.

background image

class Kot
{
public:
unsiqned int nWiek;
unsigned int nWaga;

Miaucz () ;
};

Kot oFilemon;
oFilemon.nWiek = 8 ;
oFilemon.nWaga = 18 ;
oFilemon.Miaucz() ;

Przykład

background image

class Samochod
{
public: //elementy publiczne
void Uruchom();
void Przyspieszaj();
void Hamuj();
void UstawRocznik (int nRok );
int PobierzRocznik();

private: //elementy prywatne
int nRok ;
char model[255];
}; //koniec deklaracji

Samochod oStary; //stwórz obiekt
int nKupiony; //lokalna zmienna typu int
oStary.UstawRocznik ( 84 ) ; //przypisz 84 do nRok
nKupiony = oStary.PobierzRocznik() ;//przypisz do zmiennej
//nKupiony wartość 84
oStary.Uruchom() ; //wywołaj metodę Uruchom()

Porównajmy go z tym przykładem:

background image

Implementacja metod klasy

Definicja funkcji klasy składa się kolejno z nazwy tej klasy, dwóch

dwukropków, nazwy funkcji i listy jej parametrów.

słowa

public,

które

informuje

kompilator, że wszystko co po nim
występuje ma być traktowane jako
publiczne.

PobierzWiek () pozwala na dostęp
do zmiennej prywatnej nWiek

funkcja UstawWiek() pobiera jako
parametr, wartość typu całkowitego i
przypisuje

do

zmiennej

wewnętrznej nWiek.

Zwróćmy uwagę, że funkcja main ()
nie ma możliwość bezpośredniej
zmiany wartości ani odczytania
zmiennej nWiek, ponieważ zmienna
ta jest prywatna. Funkcja main ()
wykorzystuje metodę PobierzWiek
()
, która, jako funkcja wewnętrzna
klasy Kot, ma dostęp do zmiennych
w klasie Kot. Dzięki tej funkcji można
przekazać wartość zmiennej nWiek
do funkcji main ().

background image

Konstruktory i destruktory

Zmienną typu int można zdefiniować na dwa sposoby. Można zdefiniować
zmienną i nadać jej wartość później, gdzieś w programie:

int nMasa; // definicja zmiennej
... // inne instrukcje
nMasa=7; // przypisanie wartości

Można również zdefiniować zmienną i od razu nadać jej wartość:

int nMasa = 7;

Inicjalizacja łączy w sobie definicję zmiennej z operacją przypisania wartości.
Nie ma żadnych przeszkód, aby wartość tę później zmienić. Inicjalizacja
zmiennej daje gwarancję, że nie będzie ona miała wartości nieustalonej.

Jak można inicjalizować zmienne wewnętrzne klasy?

Otóż w klasach występują specjalne funkcje wewnętrzne nazywane

konstruktorami

. Konstruktor może pobierać parametry, nie może

natomiast zwracać wartości (zawsze void). Konstruktor klasy musi
nazywać się tak samo jak klasa.

background image

Jeśli deklaruje się konstruktor to powinno się zadeklarować również

destruktor

.

Tak jak konstruktor tworzy i inicjalizuje obiekt, tak destruktor czyści
miejsce w pamięci (np. zwalnia zarezerwowaną pamięć). Nazwa
destruktora musi być taka jak nazwa klasy poprzedzona znakiem tyldy
(). Destruktor nie pobiera żadnych argumentów ani nie zwraca wartości.

Przykładowa deklaracja destruktora klasy Kot wygląda
tak:

Kot ();

Konstruktory domyślne

Konstruktor bez parametrów nazywany jest konstruktorem domyślnym.

Jeśli napisze się:

kompilator pozwala na pominięcie nawiasów. Zostanie wywołany domyślny
konstruktor, nie pobierający żadnych parametrów.

Kot oFilemon(5) ;

Kot oFilemon ;

to wymusza wykorzystanie konstruktora klasy Kot pobierającego jeden
parametr (w tym przypadku o wartości 5). Natomiast w przypadku takim:

background image

Konstruktory tworzone przez kompilator

Jeśli nie zadeklaruje się żadnego konstruktora, to kompilator automatycznie
stworzy konstruktor domyślny (konstruktor domyślny nie pobiera żadnych
parametrów). Domyślny konstruktor, stworzony przez kompilator, nie robi nic.
To tak, jakby się stworzyło konstruktor, nie pobierający parametrów i nie
posiadający treści:

Kot :: Kot ()
{
}

W tym miejscu należy zwrócić uwagę na dwie ważne rzeczy:

Konstruktor domyślny to dowolny konstruktor nie pobierający
parametrów. Nie ma znaczenia czy zadeklaruje się go samemu, czy
zrobi to kompilator.

Jeżeli zadeklaruje się jakikolwiek konstruktor (nie ważne czy pobiera on
parametry, czy nie) to kompilator nie stworzy konstruktora domyślnego.
W tym wypadku, jeśli chce się mieć konstruktor domyślny, trzeba go
samemu zadeklarować.

Jeżeli nie zadeklaruje się destruktora, to podobnie jak w przypadku
konstruktora, kompilator stworzy domyślny destruktor, nie wykonujący
żadnych czynności:

Kot :: Kot()

{
}

background image

Zapamiętajcie, że jeżeli zadeklarujecie konstruktor to zadeklarujcie
również destruktor (nawet jeśli jest on pusty). Nie zabiera to wiele
czasu, a czyni program bardziej eleganckim i czytelnym.

Dodany

został

konstruktor,

pobierający wartość całkowitą.

Deklaracja destruktora (destruktor
nie pobiera parametrów). Zarówno
konstruktor, jak i destruktor nigdy nie
zwraca żadnej wartości.

background image

Funkcje wewnętrzne typu const

Jeśli funkcję wewnętrzną zadeklaruje się jako const, to gwarantuje to, że
nie będzie ona zmieniać wartości żadnej zmiennej wewnętrznej danej
klasy.

Przykład

Deklaracji funkcji wewnętrznej Fun (), nie pobierającej żadnych argumentów
i zwracającej void:

void Fun () const;

Funkcje dostępu często są deklarowane jako const. Stworzona wcześniej
klasa Kot miała dwie funkcje dostępu:

void UstawWiek (int nWiek) ;
int PobierzWiek();

UstawWiek () nie może być
zadeklarowana jako const,
ponieważ zmienia wartość
zmiennej nJegoWiek.

Druga funkcja, PobierzWiek () może, a wręcz powinna być zadeklarowana
jako const, ponieważ nie modyfikuje żadnej zmiennej w klasie, a jedynie
zwraca aktualną wartość zmiennej nJegoWiek.

void UstawWiek (int nWiek) ;
int PobierzWiek()const;

background image

UWAGA: Jeżeli zadeklaruje się funkcję jako const, a późniejsza
implementacja funkcji zmieni obiekt danej klasy (poprzez zmianę
wartości należącej do klasy), to kompilator zgłosi komunikat błędu.

Jeżeli zmieni się funkcję PobierzWiek () tak, aby zliczała ile razy
odczytywaliśmy wartość zmiennej nJegoWiek (za pomocą dodatkowej
zmiennej w klasie, np. nLicznik) to

kompilator wygeneruje błąd

.

Stanie się tak, ponieważ zawartości obiektu klasy Kot zostanie zmieniona w
momencie wywołania funkcji PobierzWiek () .

Przykład

Wykorzystuj funkcje typu const wszędzie, gdzie to jest możliwe. Pozwoli
to kompilatorowi na pomoc w wyszukiwaniu błędów. Usuwanie błędów z
programu będzie przebiegać szybciej i sprawniej.

background image

Implementacja funkcji jako inline

Każdą funkcję można zadeklarować jako inline. Dotyczy to również funkcji

wewnętrznych danej klasy. Słowo kluczowe inline występuje bezpośrednio

przed typem wartości zwracanej przez funkcję.

inline int Kot::PobierzWage()
{
return nJegoWaga;

//zwróć wartość zmiennej

wewnętrznej
}

Innym sposobem uzyskania funkcji typu inline jest umieszczenie definicji
funkcji bezpośrednio w deklaracji klasy. Taka definicja spowoduje
automatyczne stworzenie funkcji typu inline.

class Kot
{
public:
int PobierzWage()
{
return nJegoWaga; //

inline

};
void UstawWage ( int waga ) ;
};

background image

Interfejs a implementacja

Klientami nazywamy te części programu, które tworzą i wykorzystują
obiekty danej klasy. Interfejs klasy (deklarację) można traktować jako
kontrakt między tymi klientami. Mówi on jakie dane są dostępne w klasie
i jak klasa się zachowuje.

W deklaracji klasy Kot ustala się, że każdy Kot (obiekt tej klasy) będzie miał
zmienną wewnętrzną nJegoWiek, która może być zainicjowana poprzez
konstruktor, której wartość może być zmieniona za pomocą funkcji dostępu
UstawWiek () i która może być odczytana za pomocą funkcji PobierzWiek ().
Gwarantuje się również, że każdy Kot będzie umiał zamiauczeć (funkcja
Miaucz () ).

Jeśli zadeklaruje się funkcję PobierzWiek () jako const (powinno się), to
gwarantuje się również, że PobierzWiek () nie zmieni zawartości obiektu Kot.

Przykład

background image

Umieszczanie deklaracji klas w plikach

nagłówkowych

Mimo ze deklaracje można umieszczać w tekstach źródłowych, to nie jest to
dobry zwyczaj. Przyjęła się konwencja umieszczania deklaracji w specjalnych
plikach, zwanych nagłówkowymi. Mają one zazwyczaj tę samą nazwę co
odpowiadający im plik źródłowy. Zmienia się jedynie rozszerzenie na .H lub
.HPP.
Przykładowo, umieszczamy deklarację klasy Kot w pliku KOT. H, a definicje
metod w pliku KOT. CPP. Aby dodać deklarację klasy do treści programu,
musimy dołączyć plik KOT. H do pliku KOT. CPP. Na początku pliku KOT. CPP
wpisujemy linię:

#include "kot.h"

Deklaracja klasy mówi kompilatorowi co to jest za klasa, jakie dane
przechowuje i jakie posiada funkcje. Deklarację klasy nazywamy jej
interfejsem ponieważ mówi ona użytkownikowi jak ma się komunikować
z klasą. Interfejs zazwyczaj jest przechowywany w plikach .H (plikach
nagłówkowych).
Definicja funkcji to informacja dla kompilatora jak dana funkcja działa.
Definicja klasy nazywana jest implementacją metody klasy i jest
przechowywana w plikach .CPP. Szczegóły implementacji dotyczą jedynie
autora klasy. Klienci klasy (czyli te fragmenty programu, które ją
wykorzystują), nie muszą wiedzieć jak funkcje są zaimplementowane.

background image

Listing zawiera implementacje
funkcji wewnętrznych klasy
Kot (wszystkie są typu inline).
Ich funkcjonalność nie uległa
zmianie (w stosunku do
poprzedniej

deklaracji

i

definicji).


Document Outline


Wyszukiwarka

Podobne podstrony:
PO wyk07 v1
postfix krok po kroku v1 1
PO wyk06 v1
PO wyk02 v1
Szkolenie Q Motion Controllers po polsku V1 2 3 11 2003
PO wyk04 v1
PO wyk01 v1
PO wyk12 v1
PO wyk08 v1
PO wyk07 v1
postfix krok po kroku v1 1
PO wyk06 v1
postfix krok po kroku v1 0
WR 1273252 v1 Skrypt po breaku 1
Rehabilitacja po endoprotezoplastyce stawu biodrowego
Systemy walutowe po II wojnie światowej
HTZ po 65 roku życia
Zaburzenia wodno elektrolitowe po przedawkowaniu alkoholu

więcej podobnych podstron