IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 74x (9.78 kB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

Předchozí článek
Aréna s bojovníky v C++
Všechny články v sekci
Objektově orientované programování v C++
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 6. až 8. lekci OOP v C++
Článek pro vás napsal Patrik Valkovič
Avatar
Uživatelské hodnocení:
24 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity