 
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:
 
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
 
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.
 
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
 
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.
 
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 () .
 
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.
 
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.
 
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).
 
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
 
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.
 
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.
 
class Kot 
{
  public:
    unsiqned int nWiek; 
    unsigned int nWaga; 
  
    Miaucz () ;
};
Kot oFilemon;
oFilemon.nWiek = 8 ;
oFilemon.nWaga = 18 ;
oFilemon.Miaucz() ;
Przykład
 
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:
 
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 (). 
 
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.
 
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:
 
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()
{
}
 
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. 
 
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;
 
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.
 
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 ) ;
};
 
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
 
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.
 
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).