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 

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 

ją 

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 

definicji). 


Document Outline