-
Ve t°φd∞ m∙╛e b²t definovßno n∞kolik konstruktor∙ (li╣φ se poΦtem a typy
parametr∙). V²jimeΦnΘ postavenφ majφ bezparametrick² konstruktor a kopφrovacφ
konstruktor. Bezparametrick² konstruktor je konstruktor bez parametr∙
(nebo konstruktor jeho╛ v╣echny parametry majφ definovanΘ implicitnφ hodnoty).
Implicitnφ verze bezparametrickΘho konstruktoru (konstruktoru vytvo°enΘho
p°ekladaΦem), pouze vyhradφ mφsto pro definovanou instanci.
Kopφrovacφ
konstruktor je konstruktor, jeho╛ jedin²m parametrem je odkaz na prom∞nnou
tΘho╛ typu, jakΘho je tento konstruktor. TakΘ kopφrovacφ konstruktor umφ
v p°φpad∞ pot°eby vytvo°it p°ekladaΦ. Tato implicitnφ verze kopφrovacφho
konstruktoru, vyhradφ pot°ebnou pam∞╗ pro nov² objekt a zkopφruje obsah
svΘho parametru do vytvß°enΘho objektu.
Nßsledujφ zßsady pou╛φvßnφ konstruktor∙:
-
Nechceme-li p°i°azovat definovanΘmu objektu ╛ßdnΘ specißlnφ poΦßteΦnφ hodnoty
ani s nφm provßd∞t n∞jakΘ parametrizovanΘ akce, m∙╛eme jej definovat klasicky
(napφ╣eme identifikßtor datovΘho typu a za n∞j identifikßtor definovanΘho
objektu). P°ekladaΦ v tomto p°φpad∞ pou╛ije bezparametrick² konstruktor.
Pokud ve t°φd∞ nenφ ╛ßdn² programßtorem definovan² konstruktor, generuje
p°ekladaΦ implicitnφ verzi bezparametrickΘho konstruktoru sßm.
-
Pokud pot°ebujeme p°i°adit definovanΘmu objektu n∞jakΘ specißlnφ poΦßteΦnφ
hodnoty nebo s nφm provßd∞t akce blφ╛e specifikovanΘ hodnotami n∞jak²ch
parametr∙, musφme zvolit jednu z nßsledujφcφch t°φ variant definic:
-
Pokud nejsou ve t°φd∞ explicitn∞ definovßny ╛ßdnΘ konstruktory a pokud
chceme polo╛kßm definovanΘho objektu pouze p°i°adit danΘ poΦßteΦnφ hodnoty,
m∙╛eme pou╛φt zp∙sob pou╛φvan² v definicφch strukturovan²ch datov²ch typ∙
(seznam hodnot jednotliv²ch polo╛ek uveden² ve slo╛en²ch zßvorkßch).
-
Pokud chceme definovanΘmu objektu p°i°adit hodnotu konstanty nebo prom∞nnΘ
n∞jakΘho typu T a ve t°φd∞ je definovßn konstruktor, jeho╛ jedin² parametr
je typu T (nebo odkaz na typ T), pak je mo╛no definovanou instanci inicializovat
tak, ╛e za jejφ identifikßtor napφ╣eme v definici rovnφtko a za n∞j onu
inicializaΦnφ hodnotu.
-
Pokud je parametrizace pot°ebnΘho konstruktoru slo╛it∞j╣φ, napφ╣eme v definici
instance jednotlivΘ skuteΦnΘ parametry konstruktoru do kulat²ch zßvorek
za identifikßtor instance.
Konstruktory se volajφ ve chvφli, kdy jsou objekty vytvß°eny. Definujeme-li
globßlnφ nebo statick² objekt, provede se konstruktor je╣t∞ p°ed inicializaΦnφmi
funkcemi (p°ed funkcemi uveden²mi v direktivßch #pragma
startup, a tφm samoz°ejm∞ i p°ed funkcφ main; inicializaΦnφ
funkce mohou poΦφtat s tφm, ╛e v╣echny statickΘ objekty jsou ji╛ p°ipraveny
k pou╛itφ). AutomatickΘ objekty (objekty, kterΘ nejsou globßlnφ ani statickΘ)
jsou vytvß°eny p°i ka╛dΘm vstupu do programu a stejn∞ tak jsou volßny i
jejich konstruktory.
Toto si nynφ ukß╛eme na p°φklad∞. Mßme nßsledujφcφ t°φdy:
class A {
public:
int a1;
int a2;
void metoda()
{};
};
class B {
public:
int bi;
A bA;
static int
pocet;
// poΦet vytvo°en²ch instancφ
B(A& a,
int i = 0){
// definice konstruktoru
bA = a;
bi = i;
pocet++;
};// Je-li
definovßn libovoln² konstruktor, pak p°ekladaΦ negeneruje bezparametrick²
konstruktor
// (v p°φpad∞ pot°eby jej musφme definovat sami, nap°. jako B() {}; nebo
jako nßsledujφcφ konstruktor)
B(int = 0,
int = 0, int =0);
B(B& b);
// kopφrovacφ konstruktor
};
int B::pocet = 0;
inline B::B(int i1,
int i2, int i3){
bi = i1;
bA.a1 = i2;
bA.a2 = i3;
pocet++;
}
B::B(B& b){
bi = b.bi;
bA.a1 = b.bA.a1;
bA.a2 = b.bA.a2;
pocet++;
}
Pro tyto t°φdy lze pou╛φvat konstruktory takto:
A as1;
// pou╛ije se implicitnφ bezparametrick² konstruktor
A as2 = {0, 2};//
zp∙sob pou╛φvan² p°i inicializaci strukturovan²ch datov²ch typ∙
const A as3 = as2;
// pou╛ije se implicitnφ kopφrovacφ konstruktor
B bs1;
// pou╛ije se B(0, 0, 0)
B bs2 = 22;
// pou╛ije se B(int, 0, 0)
const B bs3 = bs2.bi;
// pou╛ije se B(int, 0, 0)
B bs4 = bs2;
// pou╛ije se kopφrovacφ konstruktor B(B&)
B bs5 = as2;
// pou╛ije se B(A&, 0)
B bs6(22);
// pou╛ije se B(int, 0, 0)
B bs7(bs2.bA.a1);
// pou╛ije se B(int, 0, 0)
B bs8(bs2);
// pou╛ije se kopφrovacφ konstruktor B(B&)
const B bs9(as2);
// pou╛ije se B(A&, 0)
B bs10(as2, 3);
// pou╛ije se B(A&, 3)
B bs11(10, 20, 30);//
pou╛ije se B(int, int, int)
B bs12[3];
// pou╛ije se 3x B(0, 0, 0)
Pokuste se pochopit, jak tyto konstruktory pracujφ a kdy se kter² z
mo╛n²ch konstruktor∙ pou╛ije.
-
T°φda typu union nesmφ obsahovat slo╛ky, kterß majφ konstruktory,
ale sama tato t°φda konstruktory mφt m∙╛e. Vyzkou╣ejte (pokuste se deklarovat
t°φdu typu union a jako polo╛ky v nφ pou╛ijte t°φdy A a B z p°edchozφho
zadßnφ).
-
Dal╣φm typem implicitn∞ volan²ch konstruktor∙ jsou konverznφ konstruktory.
Jsou to konstruktory, kterΘ lze volat pouze s jednφm parametrem. Pou╛ijφ
se, kdy╛ pot°ebujeme inicializovat instanci danΘho objektovΘho typu a mßme
k dispozici instanci typu, pro n∞j╛ je definovßn pot°ebn² konverznφ konstruktor.
Typick²m p°φkladem je nap°. p°edßvßnφ parametr∙ funkcφm. Jednß se o ekvivalent
implicitnφho p°etypovßnφ. Uve∩me si p°φklad (p°edpoklßdßme platnost p°edchozφch
definic):
class C{
public:
B b;
C(int i=0,
int j=0, int k=0){b = B(i, j, k);};
void c(B&
bb){b = bb;};
// toto nenφ konstruktor
};
B Pokus(B b) {
// obyΦejnß funkce
return b;
// p°i p°edßvßnφ parametr∙ hodnotou a p°i p°edßvßnφ
}
// vrßcenΘ hodnoty se volß kopφrovacφ konstruktor
void main(void){
C c1(10, 20,
30);
c1.c(B(as2));
// volßnφ B::B(A&) pro p°etypovßnφ parametru
for (int i=0;
++i <=3;) bs12[i] = B(i, i*i);
bs1 = Pokus(bs12[2]);//
proto╛e se parametr p°edßvß hodnotou, musφ se
// vytvo°it
novß instance - automaticky se volß kopφrovacφ konstruktor
bs2 = Pokus(123);//
automaticky se volß konverznφ konstruktor B::B(int, 0, 0)
}
Pokuste se pochopit, kdy je volßn kter² konstruktor.
-
ObjektovΘ slo╛ky slo╛en²ch t°φd m∙╛eme inicializovat p°φmo. V hlaviΦce
konstruktoru slo╛enΘ t°φdy lze urΦit konstruktor, kter² chceme pou╛φt pro
vytvo°enφ danΘ slo╛ky. Provedeme to tak, ╛e za seznamem parametr∙ konstruktoru
slo╛enΘ t°φdy napφ╣eme dvojteΦku a za nφ uvedeme seznam identifikßtor∙
slo╛ek, kterΘ chceme vytvß°et nestandardn∞. P°i vytvß°enφ instance se jejφ
jednotlivΘ slo╛ky vytvß°ejφ v tom po°adφ, v jakΘm byly deklarovßny v definici
t°φdy. Po°adφ uvedenφ inicializßtor∙ slo╛ek v hlaviΦce konstruktoru nemß
na po°adφ jejich inicializace ╛ßdn² vliv (v dal╣φ ukßzce jsou schvßln∞
p°ehßzeny). Slo╛ky, jejich╛ inicializßtory nebudou uvedeny v hlaviΦce konstruktoru,
budou vytvo°eny bezparametrick²m konstruktorem. Slo╛ky, kterΘ ji╛ byly
inicializovßny, je mo╛nΘ pou╛φt jako parametry v inicializßtorech dal╣φch
slo╛ek.
class D{
public:
int i;
A a;
B b1;
B b2;
C c;
D(B bb, A
aa, int id2, int id3) : i(842), c(i/2, id2, id3), a(aa), b1(bb) {};
};//slo╛ka b2 nenφ
v seznamu a bude tedy inicializovßna bezparametrick²m konstruktorem
D d(bs5, as2, 246,
987);
Pole objektov²ch typ∙ m∙╛eme inicializovat stejn∞ jako pole instancφ
neobjektov²ch typ∙. Jedin² rozdφl je v tom, ╛e kdy╛ nenφ typ inicializaΦnφ
hodnoty mezi typy, pro n∞╛ je definovßn konverznφ konstruktor nebo kdy╛
pot°ebujeme p°i°adit danΘmu prvku pole hodnotu, pro jejφ╛ vytvo°enφ je
pot°ebn² konstruktor s vφce parametry, uvedeme mφsto danΘ hodnoty p°φmo
tento konstruktor (viz t°etφ a╛ ╣est² prvek pole v nßsledujφcφ ukßzce).
Stejn∞ jako u klasick²ch polφ platφ, ╛e inicializaΦnφch hodnot nesmφ b²t
vφce ne╛ je prvk∙ pole a stejn∞ jako u klasick²ch polφ nemusφme inicializovat
v╣echny prvky danΘho pole (prvky na ne╛ se inicializaΦnφ hodnoty nedostanou
budou inicializovßny bezparametrick²m konstruktorem).
B bpole[9] = {as2,
124, bs2, B(321, 765), B(as2, 654), B(bs7), B(1, 2, 3)};
Pokuste se pochopit jak inicializace objektov²ch typ∙ probφhß.
-
M∙╛eme mφt takΘ datovou slo╛ku t°φdy, kterß je odkazem. V tomto p°φpad∞
odkaz m∙╛e b²t inicializovßn pouze v inicializaΦnφm seznamu t°φdy a nikde
jinde:
class mojeTrida {
jinaTrida&
jina; // odkaz na jinou t°φdu
// ...
public:
mojeTrida();
// ...
};
mojeTrida::mojeTrida()
:
jina(*new
jinaTrida) // musφ b²t umφst∞no zde
{
}
-
Slo╛ky, kterΘ jsou deklarovßny se specifikßtorem const, jsou konstantnφ
slo╛ky. Konstantnφ slo╛ky uchovßvajφ hodnoty, kterΘ se nastavφ p°i vzniku
instance a v pr∙b∞hu jejφho ╛ivota se ji╛ nem∞nφ. M∙╛e to b²t nap°. Φas
vzniku danΘ instance, jejφ po°adovΘ Φφslo nebo jin² ·daj, kter² nebude
pot°eba m∞nit. Vytvo°te jednoduchou t°φdu s konstantnφ slo╛kou por_cis
(po°adovΘ Φφslo instance) a vyzkou╣ejte jak pracuje (v konstruktoru toto
po°adovΘ Φφslo vypisujte, vytvo°te n∞kolik objekt∙ a u n∞kterΘho z nich
se pokuste tuto slo╛ku zm∞nit).
-
Napi╣te deklaraci a implementaci t°φdy datum se soukrom²mi polo╛kami
den,
mesic
a rok, konstruktorem s implicitnφmi parametry (s nulovou hodnotou)
a metodami nastav (nastavenφ v╣ech polo╛ek),
nastavDen,
nastavMesic,
nastavRok,
Den
(zji╣t∞nφ dne), Mesic a Rok. U konstruktoru a v╣ech nastavovacφch
metod zajist∞te, v p°φpad∞ nevhodnΘ hodnoty n∞kterΘho parametru, pou╛itφ
hodnoty ze systΘmovΘho datumu (nap°. pomocφ standardnφ funkce getdate).
Toto lze vyu╛φt v p°φpad∞, kdy pou╛ijete bezparametrick² konstruktor, pak
hodnoty budou nastaveny na systΘmov² datum. Tuto t°φdu je╣t∞ dopl≥te p°etφ╛enφm
operßtoru << (v²stup datumu do datovΘho proudu). V nßsledujφcφ implementaci
tohoto operßtoru je uvedeno mnoho komentß°∙, aby jste se nauΦili definovat
takovΘto operßtory i pro svΘ vlastnφ datovΘ typy.
ostream& operator<<
(ostream& o, datum& D){
//Zapamatuj
si p∙vodnφ nastavenφ formßtovacφch p°φznak∙ a nastav v²pis v des.
//soustav∞
zarovnßvan² doprava. Hodnota ostatnφch p°φznak∙ nenφ v²znamnß.
long flags
= o.flags(ios::right + ios::dec);
//zjisti rozdφl
mezi po╛adovanou a pot°ebnou velikostφ v²pisovΘ oblasti
int width
= o.width() - 8;
//zapamatuj
si nastaven² v²pl≥ov² znak, my pou╛ijeme nulu, nap°. 01.03.99
char fill
= o.fill('0');
//pokud je
po╛adovanß oblast v∞t╣φ a pokud nebylo nastaveno zarovnßvßnφ vlevo
//zapl≥ ·vodnφ
p°ebytek p∙vodnφmi v²pl≥ov²mi znaky
if ((width
> 0) && !(flags & ios::left)){
//prom∞nnß
i z nßsledujφcφho cyklu je v tomto bloku lokßlnφ
for (int i = 0; i < width; i++, o << fill);
}
//v²pis datumu
o <<
setw(2) << D.Den() << "." << setw(2) << D.Mesic()
<< "."
<< setw(2) << (D.Rok()%100);
//obnova p∙vodnφho
nastavenφ zapln∞nφ zßv∞reΦnΘho p°ebytku v²pl≥ov²mi znaky
o.fill(fill);
o.flags(flags);
if ((width
> 0) && (flags & ios::left)){
for (int i = 0; i < width; i++, o << fill);
}
return o;
}
Vytvo°enou t°φdu vyzkou╣ejte v n∞jakΘm programu.
-
Stejn∞ jako datovΘ slo╛ky t°φdy mohou b²t se specifikßtorem static,
mohou b²t s tφmto specifikßtorem i metody t°φdy. Definujφ se stejn∞ jako
b∞╛nΘ metody, pouze musφme mφt na pam∞ti, ╛e nejsou svßzßny s ╛ßdnou instancφ
(jednß se vlastn∞ o funkci) a nelze v nich tedy pou╛φvat klφΦovΘ slovo
this.
Tyto metody m∙╛eme kvalifikovat stejn∞ jako slo╛ky t°φdy se specifikßtorem
static,
bu∩ prost°ednictvφm identifikßtoru n∞jakΘ instance (ten odd∞lujeme teΦkou)
nebo prost°ednictvφm identifikßtoru t°φdy (odd∞lujeme jej dv∞ma dvojteΦkami).
Metody se specifikßtorem static se pou╛φvajφ kdy╛ pot°ebujeme pracovat
pouze se slo╛kami static, kdy╛ chceme omezit p°φstupovß prßva k
danΘ funkci na metody danΘ t°φdy nebo kdy╛ pot°ebujeme n∞jakou funkci,
kterß nem∙╛e b²t b∞╛nou metodou, aby p°istupovala k soukrom²m slo╛kßm t°φdy.
Pokuste se vytvo°it n∞jakou t°φdu, ve kterΘ pou╛ijete metodu se specifikßtorem
static
a tuto metodu vyzkou╣ejte.
-
P°ßteli se mohou stßvat jak jednotlivΘ funkce, tak i celΘ t°φdy. Pokud
je p°φtelem deklarovanΘ t°φdy celß t°φda, vztahuje se mo╛nost p°φstupu
ke v╣em slo╛kßm deklarovanΘ t°φdy na v╣echny metody sp°ßtelenΘ t°φdy. Nositelem
sd∞lenφ o p°ßtelstvφ m∙╛e b²t jedin∞ t°φda o jejich╛ soukrom²ch polo╛kßch
se jednß. P°ßtelΘ se oznaΦujφ pomocφ klφΦovΘho slova friend, kterΘ
je nßsledovßno bu∩ prototypem funkce nebo identifikßtorem t°φdy, kterou
prßv∞ deklarovanß t°φda oznaΦuje za svΘho p°φtele. Pokud chceme zd∙raznit,
╛e deklarovan²m p°φtelem je t°φda, m∙╛eme p°ed identifikßtor sp°ßtelenΘ
t°φdy uvΘst klφΦovΘ slovo class, struct nebo union.
Nap°.
class tajna_schranka{
char *obsah;
tajna_schranka(char
*o = "") {obsah = o;};
friend class
agent;
};
V tomto p°φpad∞ jsou datovß polo╛ka a konstruktor soukromΘ a lze k
nim p°istupovat pouze ze samotnΘ t°φdy tajna_schranka a ze sp°ßtelenΘ
t°φdy agent. Vyzkou╣ejte.
-
M∙╛eme definovat i konstanty objektov²ch datov²ch typ∙. Mß to ale jeden
hßΦek: p°ekladaΦ neumφ v obecnΘm p°φpad∞ posoudit, zda n∞jakß metoda ovlivnφ
hodnotu svΘ instance Φi nikoliv a tudφ╛ nßm pro jistotu nedovolφ pro konstantu
pou╛φt ╛ßdnou metodu. JedinΘ, co s nφ m∙╛eme d∞lat, je p°edßvat ji jako
parametr funkcφm, kterΘ majφ ve svΘm prototypu u p°φslu╣nΘho parametru
specifikßtor const. Aby bylo mo╛no aplikovat na konstantnφ objekty
metody, m∙╛eme oznaΦit metody, kterΘ nem∞nφ hodnoty instance, pro kterou
je volßme, tak╛e je lze bezpeΦn∞ pou╛φt i na konstanty. Tyto metody se
v deklaraci (v definici i v prototypu) oznaΦujφ klφΦov²m slovem const,
kterΘ se uvede za zßvorkou, ukonΦujφcφ seznam parametr∙. Nap°.
class datum {
int den;
int mesic;
int rok;
public:
int Den()
const {return den;};
int Mesic()
const {return mesic;};
int Rok()
const;
};
int datum::Rok()
const{return rok;}
Vytvo°te n∞jakΘ konstantnφ instance a vyzkou╣ejte.