IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

4. diel - Deštruktory a aplikácie konštruktor v C ++

V minulej lekcii, Hracia kocka v C ++ a konštruktory , sme si popísali syntax konštruktory a to vrátane pokročilých konštrukcií ako bol poverovateľ konštruktor a volanie Konstruktor pre atribúty. Dnes si popíšeme deštruktory a ukážeme si hlavný účel, pre ktorý sú konštruktory a deštruktory použité.

Deštruktory

Podobne ako konštruktor, ktorý sa volá ihneď po vytvorení inštancie, sa destruktor volá automaticky pred zmazaním inštancie. Mazanie všeobecne prebieha na konci bloku (teda koniec funkcie alebo pri uzatvárajúca zložené zátvorke }). Destruktor sa zapisuje ako metóda, ktorá začína vlnovkou (~), po ktorej nasleduje názov triedy. Destruktor nikdy nemá parametre a nevracia hodnotu. Takýto základný destruktor nám už Visual Studio vygenerovalo a je prázdny (ak destruktor nedodáme, kompilátor automaticky vytvorí destruktor s prázdnym telom):

Kostka.h

class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();  // deklarace destruktoru
    int pocet_sten;
};

Kostka.cpp

Kostka::~Kostka()  // prázdný destruktor
{
}

Aby sme videli, kedy sa destruktor volá, pridáme si do jeho implementácie výpis do konzoly:

#include <iostream>  // pokud v souboru chybí
using namespace std;  // pokud v souboru chybí
Kostka::~Kostka()
{
    cout << "Volani destruktoru pro kostku s " << pocet_sten << " stenami" << endl;
}

Do main.cpp si vložíme nasledujúci kód, ktorý ukazuje prípady, kedy sa destruktor volá.

void funkce(Kostka k)
{
    cout << "Funkce" << endl;
}
int main()
{
    Kostka prvni(1);
    if (true)
    {
        Kostka druha(2);
        funkce(druha);
        cout << "Funkce skoncena" << endl;
    }
    // cin.get();
    return 0;
}
#include <iostream>
#ifndef __KOSTKA_H__
#define __KOSTKA_H__
using namespace std;
class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int pocet_sten;
};
#endif
#include <iostream>
#include "Kostka.h"
using namespace std;
Kostka::Kostka() : Kostka(6)
{
    cout << "Volani bezparametrickeho konstruktoru" << endl;
}

Kostka::Kostka(int _pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    pocet_sten = _pocet_sten;
}

Kostka::~Kostka()
{
    cout << "Volani destruktoru pro kostku s " << pocet_sten << " stenami" << endl;
}

Výpisy vidíme na výstupe aplikácie:

Konzolová aplikácia
Volani konstruktoru s parametrem
Volani konstruktoru s parametrem
Funkce
Volani destruktoru pro kostku s 2 stenami
Funkce skoncena
Volani destruktoru pro kostku s 2 stenami
Volani destruktoru pro kostku s 1 stenami

Keď si príklad rozoberieme, zistíme, že destruktor sa volá pred ukončovacími zloženými zátvorkami a na konci funkcie. Vtedy už nie je premenná potreba a C ++ vykoná jej odstránenie z pamäte. Pre lepšie pochopenie prikladám kód s komentármi.

void funkce(Kostka k)
{
    cout << "Funkce" << endl;
} // destruktor pro "k"

int main()
{
    Kostka prvni(1); // první konstruktor
    if (true)
    {
        Kostka druha(2); // druhý konstruktor
        funkce(druha);
        cout << "Funkce skoncena" << endl;
    } // destruktor pro "druha"
    // cin.get(); pokud volání necháme, neuvidíme mazání "prvni"
    return 0;
} // destruktor pro "prvni"

Konštruktory sú vypísané tiež, pretože sme ponechali kód z minulej lekcie. Mohlo by vás zaraziť, že sú volány tri deštruktory, ale iba dva konštruktory. V jednom prípade sa volá kopírujúci konštruktor, ale tým sa budeme zaoberať v inej lekciu. Zatiaľ nám stačí vedieť, kedy je destruktor volaný.

Konštruktor pre inicializáciu

Teraz sa pozrieme na jeden prípad, kedy sa nám konstruktory hodí - inicializácia triedy.

Definujme na kocke metódu hod(), ktorá nám vráti náhodné číslo od 1 do počtu stien. Je to veľmi jednoduché, metóda nebude mať žiadny parameter a návratová hodnota bude typu int. Náhodné číslo získame tak, že zavoláme funkciu rand() z knižnice cstdlib.

Kostka.h

#ifndef __KOSTKA_H__
#define __KOSTKA_H__

class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int hod();
    int pocet_sten;
};
#endif

Kostka.cpp

#include <iostream>
#include <cstdlib>
#include "Kostka.h"
using namespace std;
// ... již definované metody
int Kostka::hod()
{
    return rand() % pocet_sten + 1;
}

rand() vracia pseudo náhodné číslo. Aby bolo v požadovanom rozsahu, musíme naň použiť %pocet_sten. Jednička sa pripisuje preto, aby náhodné čísla bola od jednotky a nie od nuly. Pseudonáhodnej číslo znamená, že sa začne na nejakom čísle a nejakú operácií sa od neho dopočítavajú zostávajúce čísla. To má jednu nevýhodu - do main.cpp napíšte nasledujúci kód (ten pôvodný môžete zmazať):

#include <iostream>
#include "Kostka.h"
#include "Arena.h"

using namespace std;


int main()
{
    Kostka kostka;
    for (int i = 0; i < 10; i++)
        cout << kostka.hod() << " ";
    cin.get();
    return 0;
}

Všimnite si, že ak program spustíme opakovane, vždy generuje rovnaké čísla (aj keď by ich mal generovať náhodne). To je z dôvodu, že východisková číslo pre generovanie je zakaždým rovnaké. My potrebujeme, aby sa pri každom spustení začínalo od iného čísla, toho docielime pomocou metódy srand(), do ktorej odovzdáme aktuálny čas. A pretože to vlastne nastavuje inštanciu, tento kód vložíme do konstruktoru.

Pozn .: Vedľa knižnice cstdlib musíme includovat aj knižnicu ctime.

//main.cpp
int main()
{
    Kostka kostka;
    for (int i = 0; i < 10; i++)
        cout << kostka.hod() << " ";
    cin.get();
    return 0;
}
#ifndef __KOSTKA_H__
#define __KOSTKA_H__

class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int pocet_sten;
    int hod();
};
#endif
#include "Kostka.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

Kostka::Kostka() : Kostka(6)
{
    cout << "Volani bezparametrickeho konstruktoru" << endl;
}

Kostka::Kostka(int _pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    pocet_sten = _pocet_sten;
    srand(time(NULL));
}
// zbývající metody

Kostka::~Kostka()
{
    cout << "Volani destruktoru pro kostku s " << pocet_sten << " stenami" << endl;
}

int Kostka::hod()
{
    return rand() % pocet_sten + 1;
}

Teraz kocka vždy vygeneruje iné čísla a tým sme hotoví.

Konštruktor pre správu pamäte

Druhým prípadom, kedy môžeme použiť konštruktor (a destruktor), je pre správu pamäte. Vďaka tomu, že sa konštruktory a deštruktory volajú automaticky, máme istotu, že sa kód vždy vykoná. Môžeme si teda v konstruktoru alokovať pamäť a v destruktor ju zase zmazať. Pre príklad si zoberieme našu arénu, v ktorej sú aktuálne dva bojovníci. Povedzme, že chceme zadať počet bojovníkov v parametri - musíme si dynamicky vytvoriť pole bojovníkov. Súbor Arena.h upravíme nasledovne:

#ifndef __ARENA_H_
#define __ARENA_H_
#include "Hrac.h"

class Arena
{
public:
    Hrac** hraci;
    int pocet_hracu;
    Arena(int _pocet_hracu); // byl změněn název parametru
    ~Arena();
};
#endif

Dvoch hviezdičiek sa nebojte - je to pole ukazovateľov na Hrac (nemôžeme vytvoriť len pole hráčov, pretože nemáme predvolená = bezparametrický konštruktor, ktorý je potreba). V konstruktoru toto pole alokuje, podľa počtu hráčov sa opýtame na mená a hráča vytvoríme. V destruktor potom vykonáme opačnú operáciu a všetko zmažeme. Na kód sa môžete pozrieť:

Arena.cpp

#include <iostream>
#include "Arena.h"

using namespace std;

Arena::Arena(int _pocet_hracu)
{
    pocet_hracu = _pocet_hracu; // uložení počtu hráčů
    hraci = new Hrac*[pocet_hracu]; // vytvoření pole pro hráče
    for (int i = 0; i < pocet_hracu; i++)
    {
        string jmeno;
        cout << "Zadejte jmeno hrace: ";
        cin >> jmeno;
        hraci[i] = new Hrac(jmeno); // vytvoření hráče
    }
}

Arena::~Arena()
{
    for (int i = 0; i < pocet_hracu; i++)
        delete hraci[i]; // mazání hráčů
    delete[] hraci; // mazání pole
    hraci = NULL;
}

Ak by sme pamäť nemazali, zostávala by alokovaná a nemali by sme sa k nej ako dostať (neexistoval by na ňu ukazovateľ) a nebolo by ju možné zmazať teda ani neskôr. Ak by prebiehalo vytváranie inštancií napríklad v cykle, potom by program začal konzumovať stále viac a viac RAM, kým by ju nezabral celú (a mať rádovo gigabajty RAM pre takú malú aplikáciu je už trochu zvláštne). Ak nie je voľná RAM pamäť a program požiada o ďalšie, operačný systém už nemá čo prideliť a aplikáciu zhodí. Preto, ak sa vám stáva, že aplikácia po nejakej dobe spadne, skúste si skontrolovať, koľko zaberá miesta v pamäti a ak sa toto miesto neustále zväčšuje, zrejme niekde neuvoľňuje pamäť - tzv. Memory leak.

Main.cpp

Tým máme hotovú arénu a môžeme ju použiť v main.cpp:

#include <iostream>
#include "Kostka.h"
#include "Arena.h"

using namespace std;


int main()
{
    Kostka kostka;
    for (int i = 0; i < 10; i++)
        cout << kostka.hod() << " ";
    cout << endl;

    Arena arena(4);
    cin.get();
    return 0;
}

výsledok:

Konzolová aplikácia
Volani konstruktoru s parametrem
Volani bezparametrickeho konstruktoru
2 6 1 2 1 6 2 3 1 4
Zadejte jmeno hrace: Pavel
Zadejte jmeno hrace: Karel
Zadejte jmeno hrace: Zdenek
Zadejte jmeno hrace: Lukas
Volani destruktoru pro kostku s 6 stenami

Všetko funguje podľa našich predstáv. Alokovanie a uvoľňovanie pamäte je najčastejšia vec, ktorá sa v konstruktoru a deštruktory vykonáva, preto odporúčam si posledný ukážku poriadne prejsť a pochopiť, ako to vlastne funguje.

To je pre túto lekciu všetko a nabudúce, Riešené úlohy k 3. a 4. lekciu OOP v C ++ , si odstránime tie škaredé názvy parametrov začínajúcich podčiarknikom. Zdrojové kódy z dnešnej lekcie sú priložené pod článkom na stiahnutie ako vždy.

V nasledujúcom cvičení, Riešené úlohy k 3. a 4. lekciu OOP v C ++, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 107x (7.21 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++

 

Predchádzajúci článok
Hracia kocka v C ++ a konštruktory
Všetky články v sekcii
Objektovo orientované programovanie v C ++
Preskočiť článok
(neodporúčame)
Riešené úlohy k 3. a 4. lekciu OOP v C ++
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity