Lekce 8 - Konstantní metody v C++
V minulé lekci, Aréna s bojovníky v C++, jsme dokončili naši objektovou arénu.
V dnešním tutoriálu zjistíme, co jsou to konstantní metody a proč se jimi zabývat.
Konstantní metody
Jak již název napovídá, konstantní metoda je taková metoda, která
nemění data instance. Jsou to všechny gettery - pouze získávají data, nic
nemění. Z toho důvodu by správně měly být všechny gettery označeny jako
konstantní. Metoda Bojovnik.nazivu()
je také svým způsobem
getter, protože pouze zjišťuje zda je dostatečný počet životů. Všechny
metody, které nemění data, bychom měli označit jako konstantní - to
uděláme jednoduše přidáním klíčového slova const
za název
metody (do .h i .cpp souboru). Například pro třídu
Bojovnik
:
Bojovnik.h
#ifndef __BOJOVNIK_H_ #define __BOJOVNIK_H_ #include <string> #include "Kostka.h" using namespace std; class Bojovnik { private: float zivot; float max_zivot; float utok; float obrana; Kostka &kostka; public: Bojovnik(float zivot, float utok, float obrana, Kostka &kostka); bool nazivu() const; float utoc(Bojovnik &druhy) const; float getZivot() const; float getMaxZivot() const; float getUtok() const; float getObrana() const; }; #endif
Bojovnik.cpp
#include "Bojovnik.h" Bojovnik::Bojovnik(float zivot, float utok, float obrana, Kostka &kostka) : kostka(kostka), zivot(zivot), max_zivot(zivot), utok(utok), obrana(obrana) {} bool Bojovnik::nazivu() const { return this->zivot > 0; } float Bojovnik::utoc(Bojovnik & druhy) const { float obrana_druhy = druhy.obrana + druhy.kostka.hod(); float utok_prvni = this->utok + this->kostka.hod(); float zraneni = utok_prvni - obrana_druhy; if (zraneni < 0) zraneni = 0; druhy.zivot -= zraneni; return zraneni; } float Bojovnik::getZivot() const { return this->zivot; } float Bojovnik::getMaxZivot() const { return this->max_zivot; } float Bojovnik::getUtok() const { return this->utok; } float Bojovnik::getObrana() const { return this->obrana; }
Všimněte si, že i metoda utoc()
je konstantní - nemění
data instance, ale mění data parametru.
Pravidla
Proč se tím zabýváme? Klíčové slovo const
nám pohlídá,
že je metoda správně implementovaná. Pokud bychom pro třídu
Kostka nadefinovali metodu setPocetSten()
a nastavili ji
jako konstantní, kompilátor by nám zahlásil následující hlášku (pro
Visual Studio):
error C2228: left of '.pocet_sten' must have class/struct/union note: type is 'const Kostka *const ' note: did you intend to use '->' instead?
Kompilátor si sám pohlídá, že funkce nemůže nic změnit a ostatní programátory tím informujeme, že data jsou v bezpečí (nemohou se změnit).
Jaká další pravidla platí? Z konstantní metody můžeme volat pouze
konstantní metody. To znamená, že ve chvíli, kdy nastavíme getter jako
konstantní, nemůžeme z něj zavolat setter (jinak by klíčové slovo
const
nedávalo smysl - modifikovala by se data).
Existuje ještě třetí a nejdůležitější pravidlo: na konstantní objekty lze volat pouze konstantní metody. Například pro našeho bojovníka. Předpokládáme, že následující kód by měl fungovat:
const Bojovnik bojovnik(100,8,5,kostka); float utok = bojovnik.getUtok(); float obrana = bojovnik.getObrana(); float uroven_agresivity = utok - obrana;
Proč bychom to očekávali? Ačkoliv je bojovník konstantní, chceme získat útok a obranu pouze pro čtení - to by neměl být problém, protože metody nemění data a konstanta je dodržena. To ale víte vy, ale zatím to neví kompiler. Aby tento kód fungovat, musíte nastavit gettery jako konstantní - kompiler má potom jistotu, že metoda nic nezmění a může být tedy volána nad konstantním objektem.
Ukazatele
Pro ukazatele a reference se situace ještě trochu zkomplikuje. Přidáním
klíčového slova const
za název metody se stane, že jsou (pro
představu) všechny atributy označeny jako konstantní. Pro ukázku si
představme následující třídu:
class Uzivatel { int vek; char* jmeno; void vypisJmeno() const; };
Uvnitř metody vypisJmeno()
bude díky const
ukazatel this
typu Uzivatel const * const
(na rozdíl
od klasického Uzivatel * const
). Jen pro upřesnění, ukazatele
vždy čteme odzadu, tedy Uzivatel const * const
je "konstantní
ukazatel na konstantního uživatele", zatímco Uzivatel * const
je
"konstantní ukazatel na uživatele". Také máme dvě možnosti zápisu, tedy
následující dvě ukázky jsou ekvivalentní: Uzivatel const *
a
const Uzivatel *
.
Konstantní metodu si lze tedy představit tak, že všechny atributy budou konstantní:
class Uzivatel { const int vek; char * const jmeno; };
Všimněte si typu atributu jmeno
. Jedná se o konstantní
ukazatel (nemůžeme změnit adresu, kam ukazuje), ale není to ukazatel na
konstantní hodnotu - jméno můžeme stále změnit. Konstantní metody nám
nezaručí, že se nezmění data instance - zaručí nám pouze to, že se
nezmění atributy.
Na problémy dojde i při práci s referencemi. Představme si, že chceme
vrátit věk uživatele referencí. Není to obvyklé, ale z nějakého důvodu
to tak chceme. Referenci na int
použít nemůžeme, protože typ
atributu je const int
a typy se musí shodovat. Z toho plyne, že
musíme použít konstantní referenci:
class Uzivatel { int vek; char* jmeno; void vypisJmeno() const; const int& getVek() const; };
Nyní by naše část kódu fungovala, a když se nad tím zamyslíme, pak teprve teď funguje správné - respektuje konstantní hodnoty tam, kde to dává smysl.
To by bylo pro dnešní kratší lekci vše. Ve zdrojovém byly přidány konstanty ke getterům a k pár dalším metodám (u kterých to dávalo smysl). Příště budeme s kódem pokračovat a proto bych doporučil stáhnout si zdrojové kódy dole pod článkem. Budete mít jistotu, že budeme pracovat se stejným zdrojovým kódem.
V následujícím cvičení, Řešené úlohy k 6. až 8. lekci OOP v C++, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 75x (9.78 kB)
Aplikace je včetně zdrojových kódů v jazyce C++