Konstruktory
Již v předchozím díle jsme si o konstruktoru řekli následující tři důležité poznatky:
- Konstruktor je speciální metoda třídy, která se volá automaticky při vytváření nového objektu
- Konstruktor se vždy jmenuje stejně jako třída
- 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:
- Jmenuje se stejně jako třída, jen s tím rozdílem, že před jeho názvem je znak ~ (tilda, vlnovka)
- Nemá žádný návratový typ – ani void
- Volá se automaticky před skončením platnosti objektu
- Destruktor nesmí mít žádné parametry
- 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.