-
V tΘto kapitole budeme vyvφjet slo╛it∞j╣φ konzolovou aplikaci. Jednß se
o jednoduchou databßzi, ve kterΘ si ukß╛eme komplexn∞j╣φ pou╛itφ virtußlnφch
funkcφ. Nejprve vytvo°φme deklaraci a implementaci t°φdy osoba.
Tato t°φda bude obsahovat dv∞ chrßn∞nΘ datovΘ slo╛ky a jednu virtußlnφ
metodu.
class osoba {
protected:
char jmeno[25];
int plat;
public:
virtual void
zobraz(void);
};
void osoba::zobraz(void){
cout <<
"osoba::zobraz - chyb∞jφcφ metoda v odvozenΘ t°φd∞\n";
}
Implementace tΘto t°φdy je snadnß, bude obsahovat pouze implementaci
virtußlnφ metody zobraz. Tato t°φda bude zßkladem pro odvozovßnφ
dal╣φch specializovan²ch t°φd, ale nikdy ji nebudeme pou╛φvat (budeme ji
pou╛φvat pouze jako t°φdu p°edka). Z tohoto d∙vodu by metoda zobraz
nem∞la b²t nikdy volßna a do jejφ implementace vlo╛φme pouze v²pis signalizace
chyby.
Dßle vytvo°φme t°i odvozenΘ t°φdy. Jsou to t°φdy vedouci, programator
a sekretarka. V╣echny obsahujφ metodu zobraz, kterß je stejnß
jako tato metoda v t°φd∞ p°edka. Jsou to tedy virtußlnφ metody.
class vedouci : public
osoba {
char titul[25];
public:
void inicializace(char
jm[], int pl, char ti[]);
void zobraz(void);
};
class programator
: public osoba {
char titul[25];
char jazyk[25];
public:
void inicializace(char
jm[], int pl, char ti[], char ja[]);
void zobraz(void);
};
class sekretarka
: public osoba {
char tesnopis;
int rychlost_psani;
public:
void inicializace(char
jm[], int pl, char te, int ry);
void zobraz(void);
};
void vedouci::inicializace(char
jm[], int pl, char ti[]){
strcpy(jmeno,
jm);
plat = pl;
strcpy(titul,
ti);
}
void vedouci::zobraz(void){
cout <<
"Vedoucφ --> " << jmeno << " mß plat
" << plat <<
" a je " << titul << ".\n\n";
}
void programator::inicializace(char
jm[], int pl, char ti[], char ja[]){
strcpy(jmeno,
jm);
plat = pl;
strcpy(titul,
ti);
strcpy(jazyk,
ja);
}
void programator::zobraz(void){
cout <<
"Programßtor --> " << jmeno << " mß plat " << plat <<
" a je " << titul << ".\n\n";
cout <<
"
" << jmeno<<" programuje v "<<jazyk<<" .\n\n";
}
void sekretarka::inicializace(char
jm[], int pl, char te, int ry){
strcpy(jmeno,
jm);
plat = pl;
tesnopis =
te;
rychlost_psani
= ry;
}
void sekretarka::zobraz(void){
cout <<
"Sekretß°ka --> " << jmeno << " mß plat " << plat
<< ".\n\n";
cout <<
"
" << jmeno << " pφ╣e rychlostφ " <<
rychlost_psani
<< " znak∙ za minutu a ";
if (!tesnopis)
cout << "ne";
cout <<
"ovlßdß t∞snopis.\n\n";
}
V ka╛dΘ z t∞chto t°φd nalezneme metodu inicializace, kterß provßdφ
inicializaci datov²ch slo╛ek t°φdy a virtußlnφ metodu zobraz vypisujφcφ
data t°φdy a to pro ka╛dou t°φdu jin²m zp∙sobem.
Vytvo°enΘ t°φdy nynφ pou╛ijeme v jednoduchΘm programu. Na zaΦßtku programu
je deklarace pole ukazatel∙ o 10 prvcφch, typu ukazatel na osoba.
Do tohoto pole budeme uklßdat ukazatele na odvozenΘ t°φdy. V programu alokujeme
n∞kolik objekt∙ odvozen²ch t°φd, inicializujeme je a ukazatele na n∞ vlo╛φme
do na╣eho pole. Na zßv∞r programu informace ulo╛enΘ v objektech vypφ╣eme.
Program si prostudujte a vyzkou╣ejte.
osoba *stav[10];
int main(int argc,
char **argv)
{
vedouci *ved;
programator
*prog;
sekretarka
*sekr;
ved = new
vedouci;
ved->inicializace("Karel
Velk²", 27000, "prezident");
stav[0] =
ved;
prog = new
programator;
prog->inicializace("Jan
Novßk", 21000, "analytik", "Pascal");
stav[1] =
prog;
prog = new
programator;
prog->inicializace("Ji°φ
Novotn²", 17000, "programaror", "C++");
stav[2] =
prog;
sekr = new
sekretarka;
sekr->inicializace("Marie
Hezkß", 10370, 1, 350);
stav[3] =
sekr;
ved = new
vedouci;
ved->inicializace("Jarmila
Starß", 17000, "vedoucφ ·Φetnφ");
stav[4] =
ved;
prog = new
programator;
prog->inicializace("Jan
Kluzk²",11000,"pomocn² programator", "Pascal");
stav[5] =
prog;
for (int index
= 0; index < 6; index++)
stav[index]->zobraz();
return 0;
}
-
Metodu zobraz ve t°φd∞ osoba m∙╛eme deklarovat takΘ jako
Φirou virtußlnφ metodu. ╚irß virtußlnφ metoda nemß implementaci a nezam²╣lφme
ji volat. Deklarujeme ji konstrukcφ prototyp_metody
= 0; v na╣em p°φpad∞ jde o deklaraci
virtual void zobraz(void)
= 0;
T°φda, kterß obsahuje alespo≥ jednu Φirou metodu se v terminologii
C++ oznaΦuje jako abstraktnφ. P°ekladaΦ nedovoluje definovat instance abstraktnφch
t°φd. ╚irΘ metody nemajφ definiΦnφ deklaraci. Pokus o volßnφ ΦirΘ metody
skonΦφ chybou. Zm∞≥te t°φdu osoba na abstraktnφ a vyzkou╣ejte.
-
V tomto zadßnφ budeme pokraΦovat ve v²voji databßze zam∞stnanc∙. Nßsleduje
deklarace a implementace dal╣φch dvou t°φd, kterΘ vyu╛ijeme k vytvo°enφ
spojovΘho seznamu zam∞stnanc∙. Pov╣imn∞te si, ╛e prvky spojovΘho seznamu
neobsahujφ data, ale ukazatele na t°φdu osoba, kterou jsme vytvo°ili
v p°edchozφm programu, a m∙╛eme tedy vytvo°it spojov² seznam prvk∙ t°φd
osoba
a to bez nutnosti modifikace tΘto t°φdy. V tomto souboru si pov╣imn∞te
pou╛itφ dop°ednΘ deklarace t°φdy seznam_zamest (musφme ji pou╛φt,
nebo╗ ob∞ t°φdy se odkazujφ na sebe navzßjem). Dßle si pov╣imn∞te poslednφho
°ßdku v deklaraci t°φdy prvek_seznamu (friend
class seznam_zamest;), kter² dßvß t°φd∞ seznam_zamest
voln² p°φstup k polo╛kßm t°φdy prvek_seznamu. Je to nutnΘ, proto╛e
metoda pridej_osobu musφ p°istupovat k polo╛ce dalsi.
class seznam_zamest;
// Dop°ednß deklarace
class prvek_seznamu
{
// Jeden prvek z°et∞zenΘho seznamu
osoba *data;
prvek_seznamu
*dalsi;
public:
prvek_seznamu(osoba
*novy_zamest){
dalsi = NULL;
data = novy_zamest;
};
friend class
seznam_zamest;
};
class seznam_zamest{
// Z°et∞zen² seznam
prvek_seznamu
*zacatek;
prvek_seznamu
*konec;
public:
seznam_zamest()
{zacatek = NULL;}
void pridej_osobu(osoba
*novy_zamest);
void zobraz_seznam(void);
};
void seznam_zamest::pridej_osobu(osoba
*novy_zamest){
prvek_seznamu
*pom;
pom = new
prvek_seznamu(novy_zamest);
if (zacatek
== NULL)
zacatek = konec = pom;
else {
konec->dalsi = pom;
konec = pom;
}
}
void seznam_zamest::zobraz_seznam(void){
prvek_seznamu
*pom;
pom = zacatek;
do {
pom->data->zobraz();
pom = pom->dalsi;
} while (pom
!= NULL);
}
Databßze zam∞stnanc∙ je realizovßna pomocφ spojovΘho seznamu, kter²
vytvß°φme prost°ednictvφm na╣ich dvou nov²ch t°φd. Program je jednoduch²
a nepot°ebuje ╛ßdnΘ vysv∞tlenφ. Prostudujte si jej a vyzkou╣ejte jej.
seznam_zamest seznam;
int main(int argc,
char **argv)
{
vedouci *ved;
programator
*prog;
sekretarka
*sekr;
ved = new
vedouci;
ved->inicializace("Karel
Velk²", 27000, "prezident");
seznam.pridej_osobu(ved);
prog = new
programator;
prog->inicializace("Jan
Novßk", 21000, "analytik", "Pascal");
seznam.pridej_osobu(prog);
prog = new
programator;
prog->inicializace("Ji°φ
Novotn²", 17000, "programaror", "C++");
seznam.pridej_osobu(prog);
sekr = new
sekretarka;
sekr->inicializace("Marie
Hezkß", 10370, 1, 350);
seznam.pridej_osobu(sekr);
ved = new
vedouci;
ved->inicializace("Jarmila
Starß", 17000, "vedoucφ ·Φetnφ");
seznam.pridej_osobu(ved);
prog = new
programator;
prog->inicializace("Jan
Kluzk²",11000,"pomocn² programator", "Pascal");
seznam.pridej_osobu(prog);
seznam.zobraz_seznam();
return 0;
}
-
Do na╣eho programu p°idejte deklaraci a implementaci novΘ t°φdy nazvanΘ
poradce
(p°idanΘ slo╛ky si zvolte) a p°idejte p°φkazy na vyzkou╣enφ pou╛itφ novΘ
t°φdy.
-
V p°edchozφ kapitole jsme se seznßmili s vφcenßsobnou d∞diΦnostφ. P°i vφcenßsobnΘ
d∞diΦnosti mohou ale vznikat problΘmy. Nap°. pro vstupy a v²stupy v C++
pou╛φvßme datovΘ proudy. Zßkladem datov²ch proud∙ je t°φda ios.
Tato t°φda definuje vlastnosti spoleΦnΘ v╣em datov²m proud∙m (stavovΘ p°φznaky,
formßtovacφ p°φznaky apod.). Od t°φdy ios je odvozena °ada specializovan∞j╣φch
t°φd. Jednß se takΘ o t°φdy istream a ostream, kterΘ definujφ
vstupnφ a v²stupnφ datovΘ proudy. SpoleΦn²m potomkem t∞chto obou t°φd je
t°φda iostream pro proudy, kterΘ umo╛≥ujφ zßrove≥ vstup i v²stup
dat. iostream tedy d∞dφ v╣e od istream a ostream a
nic dal╣φho nep°idßvß. Z pravidel vφcenßsobnΘho d∞d∞nφ vypl²vß, ╛e iostream
bude obsahovat dva zd∞d∞nΘ podobjekty t°φdy ios a tedy i dvakrßt
formßtovacφ a stavovΘ p°φznaky, co╛ nenφ v∙bec vhodnΘ. Jazyk C++ nabφzφ
°e╣enφ v podob∞ tzv. virtußlnφho d∞d∞nφ. To zajistφ, ╛e vφcekrßt zd∞d∞nΘ
prvky se slouΦφ. Deklarujeme-li t°φdu ios jako virtußlnφho p°edka
t°φd istream a ostream, bude jejich spoleΦn² potomek iostream
obsahovat pouze jeden podobjekt typu ios.
Virtußlnφho p°edka vytvo°φme tak, ╛e p°i specifikaci p°edka pou╛ijeme
klφΦovΘ slovo virtual. Nßsleduje p°φklad pou╛itφ:
class a {
double x;
public:
a(){};
};
class aa : public
virtual a {
double a;
public:
aa() {};
};
class ab : public
virtual a {
double b;
public:
ab() {};
};
class X : public
aa, public ab {
double xx;
};
Instance t°φdy X bude nynφ obsahovat pouze jeden podobjekt t°φdy
a.
Vyzkou╣ejte.
-
Jednou z v²hod, kterΘ nßm C++ nabφzφ, je mo╛nost p°et∞╛ovat nejen funkce,
ale i p°evß╛nou v∞t╣inu operßtor∙. Nelze p°et∞╛ovat operßtory: .
(teΦka), .* (teΦka_hv∞zdiΦka), :: (dv∞ dvojteΦky), ? :
(podmφn∞n² v²raz), sizeof, typeid, dynamic_cast, static_cast,
reinterpret_cast
a const_cast. Z hlediska p°et∞╛ovßnφ lze rozd∞lit operßtory na t°i
skupiny. Prvnφ skupinu tvo°φ operßtory, kterΘ m∙╛eme p°et∞╛ovat pouze jako
nestatickΘ metody objektov²ch typ∙. Pat°φ sem operßtory () (volßnφ
funkce), [] (indexovßnφ), ->
(nep°φmΘho p°φstupu), =
(prostΘ p°i°azenφ) a operßtor p°etypovßnφ
(typ). Druhou (nejrozsßhlej╣φ)
skupinu tvo°φ operßtory, kterΘ lze p°et∞╛ovat jako nestatickΘ metody objektov²ch
typ∙ nebo jako °adovΘ funkce. Jsou to v╣echny operßtory, kterΘ nejsou v
ostatnφch skupinßch a nejsou operßtorem, kter² nelze p°et∞╛ovat. Tyto operßtory
musφ mφt alespo≥ jeden operand objektovΘho nebo v²ΦtovΘho typu. Poslednφ
skupinu tvo°φ operßtory pro sprßvu pam∞ti new a delete. Platφ
pro n∞ zvlß╣tnφ pravidla. Nelze zm∞nit chovßnφ operßtor∙ pro argumenty
standardnφch datov²ch typ∙. Pro p°et∞╛ovßnφ operßtor∙ pou╛φvßme takΘ termφn
homonyma operßtor∙. Nejprve se seznßmφme s obecn²mi pravidly p°et∞╛ovßnφ:
-
Homonyma operßtor∙ majφ stejnou prioritu a asociativu jako odpovφdajφcφ
standardnφ operßtory.
-
Homonyma operßtor∙ zachovßvajφ poΦet operand∙ jak² je u standardnφch operßtor∙.
-
Pro p°etφ╛enΘ operßtory nesmφme definovat implicitnφ hodnoty parametr∙.
-
Nelze zavßd∞t novΘ operßtory (nap°. **).
-
Pokud p°etφ╛φme operßtor jako nestatickou metodu, bude mφt o jeden parametr
mΘn∞, prvnφm parametrem bude *this.
-
Pokud nßm nevyhovuje definice, kterß mß jako prvnφ parametr objekt danΘ
t°φdy, nem∙╛eme definovat operßtor jako nestatickou metodu (v tomto p°φpad∞
ji zpravidla definujeme jako sp°ßtelenou funkci).
Mimo t∞chto zßsad, kterΘ jsou dßny definicφ jazyka, je vhodnΘ p°i definovßnφ
homonym operßtor∙ dodr╛ovat nßsledujφcφ omezenφ:
-
Nesna╛te se definovat homonymnφ operßtory za ka╛dou cenu. N∞kdy je daleko
v²hodn∞j╣φ pou╛itφ klasick²ch funkcφ (nap°. v²poΦet datumu uprost°ed mezi
dv∞ma daty provedeme snadn∞ji pomocφ funkce ne╛ homonymem operßtoru d∞lenφ).
-
PeΦliv∞ volte, kter² operßtor p°etφ╛φte.
-
Roz╣φ°enφ definice jednoho operßtoru neznamenß automatickΘ roz╣φ°enφ definic
operßtor∙, kterΘ s nφm n∞jak souvisejφ (nap°. roz╣φ°enφ + neovliv≥uje +=
nebo ++).
Nejprve se budeme zab²vat p°i°azovacφmi operßtory. Operßtor prostΘho p°i°azenφ
(=) definuje p°ekladaΦ implicitn∞, kdykoli je t°eba. Prost² p°i°azovacφ
operßtor m∙╛eme p°et∞╛ovat pouze jako nestatickou metodu objektovΘho typu,
zatφmco slo╛enΘ operßtory (nap°. += apod.) m∙╛eme p°et∞╛ovat i jako funkce.
P°i°azovacφ operßtor ve t°φd∞ X je metoda s prototypem
X& X::operator=(X&)
p°φpadn∞ X&
X::operator=(const X&)
Je-li a instance t°φdy X, znamenß zßpis
a = b; totΘ╛ jako
a.operator=(b);
Implicitnφ verze p°i°azovacφho operßtoru °e╣φ p°i°azovßnφ hodnot objekt∙
prost²m okopφrovßnφm jednoho objektu do druhΘho. To nßm p°estane vyhovovat
nap°. ve chvφli, kdy na╣e objekty budou rozd∞leny na n∞kolik Φßstφ svßzan²ch
navzßjem ukazateli. V nßsledujφcφm p°φkladu je ukßzka t°φdy umo╛≥ujφcφ
pracovat s °et∞zci.
class retezec {
int delka;
char *text;
void Platny(const
char* =(const char*)2, int = 0) const;
retezec&
Prirad(const char*, int);
retezec&
Pridej(const char*, int);
friend ostream&
operator<< (ostream&, const retezec&);
public:
retezec()
{text = NULL;};
//NULL indikuje je╣t∞ nep°i°azenou hodnotu °et∞zci
retezec(const
char *);
retezec(const
retezec&);
retezec(int,
const char * s) {
delka = strlen(s);
text = (char*)s;
};
~retezec(){if
(text) delete text;};
retezec&
operator= (const char* s) {return Prirad(s, strlen(s));};
retezec&
operator= (const retezec& S) {return Prirad(S.text, S.delka);};
retezec&
operator+= (const char* s) {return Pridej(s, strlen(s));};
retezec&
operator+= (const retezec& S) {return Pridej(S.text, S.delka);};
};
retezec::retezec(const
char *s) {
Platny(s,
1);
delka = strlen(s);
text = new
char[delka+1];
strcpy(text,
s);
}
retezec::retezec(const
retezec& S) {
S.Platny();
delka = S.delka;
text = new
char[delka+1];
strcpy(text,
S.text);
}
void retezec::Platny(const
char* s, int i) const { //pomocnß metoda kontrolujφcφ
korektnost operacφ
if (s &&
(i || text)) return;
cerr <<
"\n\nPou╛itφ °et∞zce bez hodnoty\n\n";
abort();
}
retezec& retezec::Prirad(const
char* s, int i) {
Platny(s,
1);
if (text)
delete text;
delka = i;
text = new
char[delka+1];
strcpy(text,
s);
return *this;
}
retezec& retezec::Pridej(const
char* s, int i) {
Platny(s);
if (*s){
char* T = new char[delka+ i+1];
strcpy(T, text);
strcpy(T+delka, s);
delka += i;
delete text;
text = T;
}
return *this;
}
inline ostream&
operator<< (ostream& o, const retezec& s) {
o <<
(void*)s.text << ": " << s.delka << ">>" << s.text
<< "<<\n";
return o;
}
int main(int argc,
char **argv)
{
retezec a
= "Karel";
retezec b("Milada");
retezec bb(b);
retezec c;
const retezec
d(1, "David");
cout <<
"a="<<a<<"b="<<b<<"c="<<c<<"d="<<d;
c = a;
cout <<
"\nc2="<<c;
cout <<
"bb2="<< (bb = "Bohuslav ");
bb += d;
cout <<
"bb3=" << bb;
cout <<
"bb4=" << (bb += " Bo╛ena");
a = bb = NULL;
// nedovolenß operace, bude vypsßna signalizace chyby
return 0;
}
Ukazatelem s hodnotou NULL v tΘto ukßzce oznaΦujeme neinicializovanou
instanci. Je v²hodnΘ, pokud m∙╛eme definovat neinicializujφcφ konstruktor
tak, aby operßtory, kterΘ cht∞jφ danou prom∞nnou pou╛φt, um∞ly poznat,
╛e dotyΦnß prom∞nnß nemß p°i°azenou hodnotu, a na tuto skuteΦnost nßs n∞jak²m
zp∙sobem upozornili. Dßle si pov╣imn∞te dvouparametrickΘho konstruktoru,
u jeho╛ prvnφho parametru nenφ uveden ╛ßdn² identifikßtor. Tento parametr
nßm slou╛φ pouze k tomu, abychom odli╣ili dan² konstruktor od druhΘho konstruktoru,
jeho╛ parametrem je takΘ textov² °et∞zec. Tφm, ╛e jsme neuvedli u parametru
jmΘno, jsme p°ekladaΦi naznaΦili, ╛e dotyΦn² parametr nehodlßme pou╛φvat
a ╛e nßs na jeho nepou╛itφ p°ekladaΦ nemß upozor≥ovat. U tohoto konstruktoru
nealokujeme pro inicializaΦnφ text mφsto v hromad∞, ale nasm∞rujeme ukazatel
p°φmo na inicializaΦnφ text (v tomto p°φpad∞ text nesmφme zm∞nit).
V ukßzce jsou vytvo°eny i operßtory p°i°azenφ. Jsou ve dvou verzφch
a to jak pro klasickΘ textovΘ °et∞zce, tak i pro °et∞zce prßv∞ deklarovanΘho
typu. T∞la funkcφ u t∞chto dvou verzφ si jsou velmi podobnß, a proto jsme
vytvo°ili pomocnΘ funkce realizujφcφ spoleΦnou Φßst algoritmu. Prostudujte
si implementaci uvedenΘ t°φdy a vyzkou╣ejte.
-
Podobn∞ jako u operßtoru p°i°azenφ m∙╛eme p°et∞╛ovat i zßkladnφ binßrnφ
operßtory (+ = * / % > < >= <= == != && || | & ^ <<
>>). Identifikßtor operßtoru je zde tvo°en klφΦov²m slovem operßtor,
za nφm╛ nßsleduje symbol danΘho operßtoru, kter² m∙╛e b²t od slova operator
odd∞len libovoln²m poΦtem mezer. Pokud binßrnφ operßtor definujeme jako
normßlnφ funkci musφ mφt dva parametry a alespo≥ jeden z nich musφ b²t
objektovΘho nebo v²ΦtovΘho typu. U operßtoru definovanΘho jako metoda je
jeho lev²m argumentem instance, jejφ╛ metodou operßtor je, tak╛e v definici
ji╛ deklarujeme pouze jeden parametr (prav² operand). Je vhodnΘ, kdy╛ binßrnφ
operßtory nem∞nφ hodnoty sv²ch operand∙ a v²sledek je p°edßvßn hodnotou
(pou╛itφ odkazu z mnoha d∙vod∙ nenφ vhodnΘ). Pou╛itφ binßrnφho operßtoru
+ si ukß╛eme na roz╣φ°enφ p°edchozφ t°φdy (retezec). Do t°φdy p°idßme:
friend retezec operator+
(const retezec&, const retezec&);
a tuto sp°ßtelenou funkci implementujeme takto:
inline retezec operator+
(const retezec& a, const retezec& b){
retezec pom
= a;
return pom
+= b;
}
Do hlavnφho programu vlo╛φme na vyzkou╣enφ nap°. tyto p°φkazy:
retezec vyrok;
vyrok = a + " a "
+ b;
cout << ("H°φ╣nφci
" + vyrok + "!!!!");
Mohli bychom deklarovat i operßtory se smφ╣en²mi parametry:
friend retezec operator+
(const char *, const retezec&);
friend retezec operator+
(const retezec&, const char *);
ale nenφ to nutnΘ. Ve t°φd∞ jsme definovali konverznφ konstruktor s
parametrem typu char *. P°ekladaΦ dφky tomu umφ zkonstruovat pomocn²
objekt, kterΘmu p°i°adφ hodnotu p°edßvanΘho °et∞zce a kter² pak p°edß operßtoru
jako skuteΦn² parametr. Dodefinovßnφm t∞chto dvou homonym operßtoru sΦφtßnφ
nep°idßme programu ╛ßdnΘ novΘ, d°φve neexistujφcφ funkce. Vyzkou╣ejte si
pou╛itφ v²╣e uvedenΘho operßtoru sΦφtßnφ.