Lekce 9 - Konstantní metody v C++

C a C++ C++ Objektově orientované programování Konstantní metody v C++

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Statika v C++, jsme se věnovali statice v C++. 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 const Uzivatel const * (na rozdíl od klasického const Uzivatel *). To si lze také představit tak, že se přidá klíčové slovo const před všechny atributy:

class Uzivatel
{
    const int vek;
    const char* jmeno;
};

Slovo "před" je velmi důležité. 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& getJmeno() 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. Příště, v lekci Přetěžování operátorů v C++, nás čeká přetěžování operátorů, takže se máte na co těšit.


 

Stáhnout

Staženo 15x (9.78 kB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

 

Článek pro vás napsal patrik.valkovic
Avatar
Jak se ti líbí článek?
1 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Miniatura
Předchozí článek
Statika v C++
Miniatura
Následující článek
Přetěžování operátorů v C++
Aktivity (4)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!