Konstruktory, destruktory a hrátky s objekty

11. díl - Konstruktory, destruktory a hrátky s objekty

Petr Sklenička       28. 5. 2011       C++/C       9821 zobrazení

V tomto díle nás čeká bližší pohled na konstruktory, zároveň si také vysvětlíme pojem destruktor. Dále se podíváme na modifikátory přístupu a nakonec si trochu pohrajeme s instancemi naší třídy Zamestnanec, kterou jsme si vytvořili v minulém díle.

Konstruktory

Již v předchozím díle jsme si o konstruktoru řekli následující tři důležité poznatky:

  1. Konstruktor je speciální metoda třídy, která se volá automaticky při vytváření nového objektu
  2. Konstruktor se vždy jmenuje stejně jako třída
  3. Konstruktor nemá žádný návratový typ – ani void

To jsou důležité věci, které je nutno vědět. Neřekli jsme si ale například to, že jedna třída může mít více konstruktorů, nemusí však také ale mít žádný. V případě, že žádný konstruktor nenapíšeme, C++ automaticky vytvoří implicitní, bezparametrický konstruktor, který ovšem nebude nic dělat. Proto je v drtivé většině případů konstruktor dobré napsat a minimálně v něm inicializovat hodnoty členských proměnných.

Z předešlého dílu máme vytvořenou třídu Zamestnanec, které jsme napsali jeden konstruktor, kterému jsme předali čtyři hodnoty – rok narození zaměstnance, počet let praxe, výši platu a jméno zaměstnance. Není problém však napsat další konstruktor, kterému budeme třeba předávat jen jméno zaměstnance a ostatní hodnoty atributů nastavíme na nulu. V tomto případě je to samozřejmě poněkud zvláštní, v praxi bychom takový konstruktor nejspíše nepotřebovali, ale ukazuji to jen jako ukázku toho, že třída může mít konstruktorů více.

#include <string>
using namespace std;

class Zamestnanec
{
    private:
        int rokNarozeni;
        int pocetLetPraxe;
        int vysePlatu;
        string jmeno;

    public:
        Zamestnanec(int rokNar, int praxe, int plat, string jm)
        {
            rokNarozeni = rokNar;
            pocetLetPraxe = praxe;
            vysePlatu = plat;
            jmeno = jm;
        }

        Zamestnanec(string jm)
        {
            jmeno = jm;
            rokNarozeni = pocetLetPraxe = vysePlatu = 0;
        }

        void ZvysPlat(int castka)
        {
            vysePlatu += castka;
        }
};

Takto vypadá třída Zamestnanec, která má dva konstruktory. V tuto chvíli je možné vytvořit její instanci těmito způsoby.

Zamestnanec A("Josef Holub");                    
Zamestnanec B(1978, 15, 32000, "Jan Krtek");

Použijeme-li první možnost, vytvoří se zaměstnanec se jménem Josef Holub, jehož atributy rok narození, plat a počet let praxe budou nastaveny na nulu.

Druhá možnost je asi jasná – zaměstnanec Jan Krtek, narozen 1978, 15 let praxe, plat 32000.

Je tedy na Vás, kolik konstruktorů třídě napíšete, jejich počet není nijak omezen. Nezapomeňte, že pokud nenapíšete žádný, vytvoří se implicitní konstruktor a objekt třídy se pak vytvoří takto:

Zamestnanec A;

Na jednu věc jsem však ještě neupozornil – jakmile vytvoříte nějaký konstruktor, poté už se žádný implicitní konstruktor automaticky nevytvoří. Pokud tedy napíšete konstruktor, do kterého budete předávat třeba čtyři hodnoty, výše uvedený zápis (Zamestnanec A;) již není možný. Došlo by k chybě během překladu.

Destruktory

Destruktor je další speciální členská funkce. Stejně jako konstruktor, tak i destruktor se volá automaticky. Asi není těžké uhodnout, že destruktor se volá v případě, končí-li platnost daného objektu. Jednoduše řečeno těsně předtím, než objekt přestane existovat.

Destruktor se opět vyznačuje následujícími rysy:

  1. Jmenuje se stejně jako třída, jen s tím rozdílem, že před jeho názvem je znak ~ (tilda, vlnovka)
  2. Nemá žádný návratový typ – ani void
  3. Volá se automaticky před skončením platnosti objektu
  4. Destruktor nesmí mít žádné parametry
  5. Třída nemůže mít více jak jeden destruktor

Ukázka destruktoru třídy Zamestnanec je zde:

~Zamestnanec()
{
    cout << "Byl zavolan destruktor.\n";
}

V praxi je samozřejmě takový destruktor k ničemu. V našem případě, kdy zatím pracujeme s jednoduchou třídou, není nutné destruktor vůbec psát, vytvoří se automaticky. Pokud bychom ale v naší třídě někde alokovali paměť pomocí new, pak by již bylo nutné v destruktoru tuto paměť uvolnit pomocí delete.

Modifikátory přístupu

Vzpomeňte si na minulý díl, kde jsem jako příklad nějakého objektu uvedl “krabičku na přeměnu střídavého proudu na stejnosměrný”. Když se na tuto krabičku podíváme, nevidíme, jaké součástky jsou přesně uvnitř, ani jaké mají hodnoty. Dalo by se tedy říct, že jsou privátní a přímo s nimi pracuje pouze ona krabička. Víme ale, k čemu krabička slouží a jak s ní pracovat.

Na tomto základě jsou postaveny právě i modifikátory přístupu – public (veřejné), private (soukromé) a protected (chráněné).

Private

K atributům, či metodám, které jsou ve třídě privátní, není možné přistupovat “zvenku”. To znamená, že s danými atributy nebo metodami může přímo pracovat pouze sama třída. Jako příklad lze uvést již zmíněnou krabičku. Jejími privátními členy jsou právě ty součástky, které jsou v ní uzavřeny. My se k nim zkrátka zvenku nedostaneme, ale krabička s nimi pracovat může.

Public

Metody a atributy označené jako public jsou veřejné, zvenku dostupné. U naší elektronické součástky můžeme za public metodu považovat převod stejnosměrného proudu na střídavý.

Protected

Tento modifikátor přístupu se používá v souvislosti s tzv. dědičností, kterou jsme ještě neprobírali (ale bude už příští díl), takže v tuto chvíli se tímto modifikátorem nebudeme zabývat.

Set a Get metody

Teď, když už víme, že k privátním atributům nelze zvenčí přistoupit, vzniká otázka, jak změnit nebo získat hodnotu takového atributu. Pro úplnost ještě ukážu, co tedy není možné udělat.

Zamestnanec A("Pepa");
A.vysePlatu = 30000;        // NELZE! Atribut je privatni

Chceme-li tedy změnit hodnotu privátního atributu, musíme napsat tzv. set metodu. Tato metoda bude veřejná, čili public, nebude nic vracet (void) a budeme ji předávat jednu hodnotu. Metoda vypadá následovně:

void NastavPlat(int plat)
{
    vysePlatu = plat;
}

Teď se asi hodně z Vás zeptá, proč tak složitě. Proč prostě neudělat atribut vysePlatu veřejný, čímž by odpadla nutnost psát set metodu. Odpověď je jednoduchá. Napíšeme-li set metodu, máme možnost, si hodnotu proměnné ohlídat. Je například jasné, že plat nemůže být záporný. Není tedy problém v metodě napsat podmínku, která si tento problém ohlídá. V případě, že bychom nechali atribut veřejný, bylo by možné nastavit výši platu na nesmyslnou hodnotu, což je nežádoucí. Proto je dobré k privátním atributům napsat vhodné set metody.

Obdobným způsobem napíšeme i get metodu, která nám hodnotu atributu vrátí. Get metoda pro atribut vysePlatu tedy bude vracet int a nebudeme ji předávat žádnou hodnotu.

int VratPlat()
{
    return vysePlatu;
}

Sami si již zkuste dopsat set a get metody zbylých tří atributů.

Za zmínku možná ještě stojí to, odkud se vzaly názvy set a get metody. Mnohé z Vás jistě napadlo, že je to z angličtiny. Set znamená nastavit, get znamená dostat. Já osobně pokud píši nějaký kód, používám anglické názvy proměnných, metod apod. Metody se pak tedy jmenují např. SetName, GetName atd. Odtud tedy název set a get metody.

Příklad na závěr

Naši třídu Zamestnanec si doplníme o další atribut. Bude to atribut, podle kterého poznáme, zda je zaměstnanec muž, či žena. Hodnotu tohoto atributu budeme předávat v konstruktoru. Set metodu pro tento atribut nebudeme psát, neboť k nastavení hodnoty dojde v konstruktoru a nepředpokládáme, že by se z muže najednou stala žena či naopak. I když samozřejmě v dnešní době už je možné skoro vše…

Naše třída by tedy v tuto chvíli měla vypadat zhruba takto:

class Zamestnanec
{
    private:
        int rokNarozeni;
        int pocetLetPraxe;
        int vysePlatu;
        string jmeno;
        bool jeMuz;
    public:
        Zamestnanec(int rokNar, int praxe, int plat, string jm, bool _jeMuz)
        {
            rokNarozeni = rokNar;
            pocetLetPraxe = praxe;
            vysePlatu = plat;
            jmeno = jm;
            jeMuz = _jeMuz;
        }
        void NastavPlat(int plat)
        {
            vysePlatu = plat;
        }
        int VratPlat()
        {
            return vysePlatu;
        }
        void ZvysPlat(int castka)
        {
            vysePlatu += castka;
        }
        bool JeZamestnanecMuz()
        {
            if (jeMuz == true)
                return true;
            return false;
        }
};

Konstruktor s jedním parametrem a destruktor jsem smazal, nebudeme je potřebovat. Všimněte si metody JeZamestnanecMuz. Na první pohled úplně obyčejná metoda, která vrátí hodnotu true, pokud je zaměstnanec muž, jinak vrátí false. Do příštího dílu Vám dám takovou malou hádanku. Tato metoda se dá napsat bez použití podmínky a lze ji napsat na jeden řádek. Můžete si na to zkusit přijít. Nakonec ale podotýkám, že způsob, jakým jsem metodu napsal výše, není špatně, rozdíl dvou řádků není až tak katastrofální. Spíš jde o to, že se s tím jednořádkovým zápisem můžete setkat v nějakém cizím kódu a nemuseli byste mu rozumět.

Máme tedy napsanou třídu, nyní si vytvoříme pět instancí – 3 ženy, 2 muže. Všechny instance uložíme do pole s názvem zamestnanci.

#include "Zamestnanec.h"
#include <iostream>
using namespace std;

int main()
{
    Zamestnanec A(1984, 7, 22100, "Jan Holub", true);
    Zamestnanec B(1981, 10, 27500, "Jana Krtkova", false);
    Zamestnanec C(1967, 24, 34200, "Bozena Fazolova", false);
    Zamestnanec D(1990, 2, 21200, "Radim Racek", true);
    Zamestnanec E(1981, 8, 23400, "Milena Hraskova", false);

    Zamestnanec zamestnanci[5] = {A, B, C, D, E};

    return 0;
}

Našim úkolem nyní bude projít celé pole a zjistit průměrný plat žen a průměrný plat mužů. Tyto informace pak vypíšeme pomocí cout. Zkuste si to nejprve sami, než se podíváte na řešení.

int main()
{
    Zamestnanec A(1984, 7, 22100, "Jan Holub", true);
    Zamestnanec B(1981, 10, 27500, "Jana Krtkova", false);
    Zamestnanec C(1967, 24, 34200, "Bozena Fazolova", false);
    Zamestnanec D(1990, 2, 21200, "Radim Racek", true);
    Zamestnanec E(1981, 8, 23400, "Milena Hraskova", false);

    Zamestnanec zamestnanci[5] = {A, B, C, D, E};

    double muziPlat = 0, zenyPlat = 0;
    int pocetMuzu = 0, pocetZen = 0;
    for (int i = 0; i < 5; i++)
    {
        if (zamestnanci[i].JeZamestnanecMuz() == true)    // je to muz
        {
            pocetMuzu++;
            muziPlat += zamestnanci[i].VratPlat();
        }
        else                                            // je to zena
        {
            pocetZen++;                                    
            zenyPlat += zamestnanci[i].VratPlat();
        }
    }

    cout << "Prumerny muzsky plat je: " << muziPlat / pocetMuzu << "\n";
    cout << "Prumerny zensky plat je: " << zenyPlat / pocetZen << "\n";

    return 0;
}

Řešení je tedy jednoduché. Postupně procházíme celé pole, přičemž vždy zjistíme, zda se jedná o muže, nebo ženu. Podle toho zvýšíme patřičnou proměnnou (pocetMuzu, pocetZen) a připočteme si plat. Až celé pole projdeme, stačí vypsat průměrné platy, které zjistíme jako podíl proměnných muziPlat a pocetMuzu, a zenyPlat a pocetZen.

Pro tento díl je to vše. Doufám, že jste vše pochopili, v opačném případě se ptejte v diskusi. V dalším díle nás čeká dědičnost.

 

hodnocení článku

3 bodů / 3 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

13. Virtuální metody 1. 2. 2012
12. Dědičnost 24. 7. 2011
11. Konstruktory, destruktory a hrátky s objekty 28. 5. 2011
10. Úvod do objektově orientovaného programování 10. 3. 2011
9. Ukazatele a odkazy, vyhledávání půlením intervalu 26. 1. 2011
8. Funkce 18. 12. 2010
7. Cykly, neboli smyčky - pokračování 19. 11. 2010
6. Cykly, neboli smyčky 26. 10. 2010
5. Pole 13. 7. 2010
4. Relační operátory a podmínky, příkaz switch 29. 6. 2010
3. Proměnné a konstanty 22. 6. 2010
2. První program 15. 6. 2010
1. Úvod, příprava na psaní aplikací v C++ 8. 6. 2010

 

Mohlo by vás také zajímat

C++/CLI a interoperabilita managed a unmanaged kódu - díl 1.: Úvod do jazyka a základní konstrukce

V tomto článku je popsána nadstavba C++ pro práci s .NET prostředím zvaná C++/CLI umožňující vytvářed mixed assembly obsahující jak managed tak unmanaged kód. V prvním díle je popsána myšlenka jazyka a základní syntaktické konstrukty (základní typy, podmínky, cykly, pole, namespace a část tříd a objektů). U čtenáře je předpokládána znalost .NET frameworku a nativního programování nejlépe v C++ (alespoň syntaxi a základy).

C++/CLI a interoperabilita managed a unmanaged kódu - díl 2.: Složitější konstrukty, low-level přístup a generické programování

V tomto článku je popsána nadstavba C++ pro práci s .NET prostředím zvaná C++/CLI umožňující vytvářed mixed assembly obsahující jak managed tak unmanaged kód. V tomto díle jsou popsány pokročilejší partie jazyka týkající se hlavně objektů, low-level přístupu k managed objektům a generického programování.

Virtuální souborový systém

Článek pojednává o způsobu návrhu virtuálního souborového systému. Před čtením Vám doporučuji stáhnout si a přečíst zadání, abyste věděli o co vlastně jde. Ke stažení jsou i zdrojové kódy v jazyce C++. Aplikace asi nemá žádné velké využití, nicméně přišlo mi to jako zajímavý problém - jednalo se o semestrální projekt.

 

 

Nový příspěvek

 

Diskuse: Konstruktory, destruktory a hrátky s objekty

Dobrý den, prosím o radu ohledně členů třídy.

Vytvořil jsem třídu zaměstnanec stejně jak máte v příkladu a dal jsem tam napřed 6 členů třídy a vše správně fungovalo, ale v případě, když jsem jich vytvořil asi 70-80 členů,tak se to překladači nelíbí a píše mi tuhle chybu: std::bad_alloc at memory location 0x0083AF14. Nemohli by jste mi někdo poradit kde dělám chybu? Nebo jak se to dá řešit jinak, když chci vytvořit více členů? Měl bych je ukládat nějak na disk?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Konstruktory, destruktory a hrátky s objekty

Dobrý den

Již několikrát jsem zde psal přízpěvky a dotazy. Mám ale teď dostdobrou otázku. Nevím jestli patří přímo k tomuto seriálu C++

(mimochodem opravdu vyjímečně dobrý seriál), ale co vše musím udělat a umět, aby ze mě byl natolik dobrý programátor, abych mohl pracovat u nějaké přední softwarové firmy (třeba: Microsoft). Abych byl jako Vy, nebo dosáhl takového úspěchu jako pan Herceg(blahopřeji). Jediné co vím je, že musím mít vysokoškolský titul. Programuju už asi rok a umím jenom opravdu základy ve VB a něco ze C++. Programátora bych chtěl dělat i jako povolání(zatím se mi daří, v září nastupuji na SŠ strojní se zaměřením na IT v Opavě). Zkrátka co pro to musím udělat.

Předem děkuji za odpověď.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud to chcete s programováním dotáhnout daleko, vysoká škola samozřejmě není vše. Vysoká škola je pouze jakýsi předpoklad, protože ani tam Vás toho až tak moc nenaučí. Musíte se tomu věnovat sám a snažit se získat praxi při tvorbě nějakých projektů, nebo začít v nějaké menší firmě, byť třeba na nějaké nižší pozici. Samozřejmě nelze začít hned s nějakým obrovským projektem, chce to všechno postupně. Pokud v dnešní době jdete někam žádat o práci, většinou se zaměstnavatelé ptají na praxi a chtějí i vidět nějaké již hotové věci, na kterých jste se podílel - proto "pouhá" vysoká škola je nedostačující, pokud máte tak vysoké ambice. A ono nakonec stále se najde dost lidí, kteří mají vysokoškolský titul a umí toho méně, než třeba člověk s maturitou. Tímto Vás ale od vysoké školy nechci nijak odrazovat.

Také si myslím, že to chce vybrat si jakýsi směr, kterým se chcete ubírat, protože programování je velmi široký pojem, každá technologie je vhodná na něco jiného a ovládat všechny na vysoké úrovni je takřka nemožné.

Píšete, že v září nastupujete na SŠ. Je dobré, že se programování věnujete už teď, čím dřív člověk začne, tím má více času na sbírání zkušeností a vědomostí (což platí obecně).

To, co jsem výše napsal, je pouze můj osobní názor, možná Vám tady někdo napíše něco jiného, proto si z každého názoru vemte to, co sám uznáte za vhodné. Nakonec Vám přeju hodně úspěchů a těší mě, že se Vám tento seriál líbí.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Nemůžu než souhlasit :)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

na Slovensku je system VS trosku iny a preto som na gymnaziu (aby som nemal problem s prijatim na VS) a programovaniu sa venujem doma vo volnom case presne odvetie programatorstva ktorym by som sa chcel uberat je Herny priemysel (konkretne Ubisoft Montreal (Kanada) ale aj Qebec neje zly (postupne sa vypracovat na generalneho riaditela :D)) s programovanim len zacinam no a podla mna sa tomu treba venovat hlavne doma pretoze neexistuje skola kde ta to naucia (jedine ze by si mal v rodine programatora ktory by ta to naucil (ja mam ale nesom s nim v kontakte :( )) takze ak sa chces presadit tak v skole mi profesorka informatiky vravela ze najdolezitejsia vec su algoritmy tak po skonceni s C++ sa vrhnem na serial venovany algoritmom tu na tomto webe a potom idem na grafiku :) neskor zvuky a prve hry :) je to dobry plan? alebo mam v nom nieco zmenit?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Konstruktory, destruktory a hrátky s objekty

Programovat jsem začal před 4 měsíci, a právě váš seriál mi pomohl dost věcí pochopit, takže podle mě velice kvalitní seriál, jen je škoda, že tento portál poslední dobou umírá, ale zpět k tématu, sice teď programuji v Javě a C#, ale ani C++ mi neni cizý :-) K té hádance jak jste dal, ta metoda mi se podle mě dala udělat takto(ternární operátor):

   bool JeZamestnanecMuz()
   {
       jeMuz == true ? return true : return false;
    }

Netestoval jsem, ale doufám, že je to tak...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pomocí ternárního operátoru to taky jde. Ve Vašem zápisu máte drobnou chybu, správně je to takto:

return jeMuz == true ? true : false;

Existuje však ještě o něco málo kratší řešení, a to úplně prosté:

return jeMuz;

Škoda jen, že mě nenapadl nějaký lepší příklad a že proměnná jeMuz je typu bool. To celou věc totiž hodně usnadňuje.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ano, samozřejmě máte pravdu, ale to jsem si uvědomil až pak, a už to nešlo opravit, metoda samozřejmě musí vracet(pokud není typu void)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Fungoval by zápis:

return jeMuz ? true : false;

?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ano, i tento zápis funguje. Jak jsem už ale výše napsal, nejjednodušší je vrátit hodnotu atributu jeMuz. Chtěl jsem však ukázat něco jiného, bohužel jeMuz je typu bool. Jako takové menší doplnění ke článku tedy uvedu, co jsem chtěl.

Potřebujeme metodu, která vrátí true, je-li počet let praxe zaměstnance roven 15. Jinak tato metoda vrátí false. Velmi intuitivně by se dalo napsat toto:

if (pocetLetPraxe == 15)
   return true;
return false;

Pokud to chceme napsat na jeden řádek, jedna z možností je použít ternární operátor. Lze ale taky napsat:

return pocetLetPraxe == 15;

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback