-
Odkazy jsou specißlnφm typem ukazatel∙, kterΘ umo╛≥ujφ pracovat s ukazateli
jako s normßlnφmi objekty. Odkazy jsou deklarovßny pomocφ operßtoru &.
Nap°.
mojeStruktura* ukStrukt
= new mojeStruktura;
mojeStruktura&
odkaz = *ukStrukt;
odkaz.X = 100;
Pov╣imn∞te si, ╛e p°i p°φstupu ke slo╛kßm struktury jsou pou╛ity p°φmΘ
selektory slo╛ky. V reßln²ch programech obvykle nenφ zapot°ebφ udr╛ovat
p°i pou╛itφ odkazu ukazatel na strukturu a p°edchozφ zßpis lze zkrßtit
takto:
mojeStruktura&
odkaz = *new mojeStruktura;
odkaz.X = 100;
Nynφ se op∞t vrßtφme k na╣φ konzolovΘ aplikaci adresß°e na╣ich znßm²ch.
╪e╣enφ z konce p°edchozφ kapitoly m∙╛eme zm∞nit takto:
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#pragma hdrstop
#include "structur.h"
//---------------------------------------------------------------------------
#pragma argsused
void zobrazZaznam(int,
adresar adrZaz);
int main(int argc,
char **argv)
{
adresar* seznam[3];
for (int i
= 0; i < 3; i++)
seznam[i] = new adresar;
cout <<
endl;
int index
= 0;
do {
adresar& zaznam = *seznam[index];
cout << "JmΘno: ";
cin.getline(zaznam.jmeno, sizeof(zaznam.jmeno)-1);
cout << "P°φjmenφ: ";
cin.getline(zaznam.prijmeni, sizeof(zaznam.prijmeni)-1);
cout << "Ulice: ";
cin.getline(zaznam.ulice, sizeof(zaznam.ulice)-1);
cout << "M∞sto: ";
cin.getline(zaznam.mesto, sizeof(zaznam.mesto)-1);
cout << "PsΦ: ";
char buff[10];
cin.getline(buff, sizeof(buff)-1);
zaznam.psc = atoi(buff);
index++;
cout << endl;
} while (index
< 3);
clrscr();
for(int i
= 0; i < 3; i++) {
zobrazZaznam(i, *seznam[i]);
}
cout <<
"Zadej Φφslo zßznamu: ";
int zaz;
do {
zaz = getch();
zaz -= 49;
} while (zaz
< 0 || zaz > 2);
adresar pom
= *seznam[zaz];
clrscr();
zobrazZaznam(zaz,
pom);
getch();
return 0;
}
void zobrazZaznam(int
cis, adresar adrZaz)
{
cout <<
"Zßznam " << (cis + 1) << ":" << endl;
cout <<
"JmΘno: " << adrZaz.jmeno << " " << adrZaz.prijmeni <<
endl;
cout <<
"Adresa: " << adrZaz.ulice << endl;
cout <<
" " << adrZaz.mesto <<
endl;
cout <<
" " << adrZaz.psc <<
endl << endl;
}
Zm∞n∞nΘ °ßdky jsou op∞t zobrazeny Φerven∞. Pov╣imn∞te si deklarace
odkazu na strukturu adresar. P°i ka╛dΘm pr∙chodu cyklem je odkazu
p°i°azen jin² objekt (nßsledujφcφ prvek v poli). Pro p°φstup k prvk∙m struktury
nynφ pou╛φvßme operßtor p°φmΘho selektoru slo╛ky. Jak uvidφme pozd∞jφ,
odkaz umo╛≥uje chßpat ukazatel jako objekt. V²sledkem pou╛φvßnφ odkazu
dostßvßme krat╣φ a Φiteln∞j╣φ k≤d.
P°esto╛e odkazy jsou preferovanΘ p°ed ukazateli, nenφ tomu tak v╛dy.
Odkazy nelze pou╛φt ve v╣ech p°φpadech. Nap°. odkaz nem∙╛e b²t deklarovßn
bez p°i°azenφ hodnoty. Musφ b²t inicializovßn p°i deklaraci. Nßsledujφcφ
k≤d zp∙sobφ chybu p°ekladu:
mojeStruktura* ukStruktura
= new mojeStruktura;
mojeStruktura&
odkaz;
odkaz = *ukStruktura;
odkaz.X = 100;
Dal╣φ problΘm s odkazem je ten, ╛e jej nelze nastavit na NULL nebo
0, co╛ u ukazalele lze.
-
Parametry u funkcφ jsme zatφm p°edßvali hodnotou (v p°edchozφ kapitole
byla ukßzka p°edßvßnφ parametr∙ odkazem). V p°φpad∞ struktur a t°φd je
ale lep╣φ p°edßvat tyto objekty odkazem. Odkazem m∙╛e b²t p°edßn libovoln²
objekt (standardnφ datovΘ typy jako je int nebo char a takΘ
instance struktur nebo t°φd). P°i p°edßvßnφ parametru funkce hodnotou je
vytvo°ena kopie objektu a funkce pracuje s touto kopiφ. Kdy╛ p°edßvßme
parametr odkazem, pak je p°edßn ukazatel na objekt a ne objekt samotn².
To mß dv∞ v²hody. Objekt p°edan² funkci m∙╛e b²t funkcφ modifikovßn a p°edßvßnφ
odkazem eliminuje re╛ijnφ nßklady na vytvo°enφ kopie objektu.
Mo╛nost modifikace objektu je d∙le╛it²m aspektem p°i p°edßvßnφ parametr∙
odkazem. Podφvejte se na nßsledujφcφ k≤d:
void Inkrementace(int&
xPos, int& yPos)
{
xPos++;
yPos++;
}
int x = 20;
int y = 40;
Inkrementace(x, y);
// x je nynφ rovno
21 a y 41
Pov╣imn∞te si, ╛e po nßvratu z funkce jsou oba p°edanΘ parametry zv∞t╣eny
o 1. To je z d∙vodu modifikace aktußlnφho objektu funkcφ prost°ednictvφm
ukazatele (nezapome≥te, ╛e odkaz je typ ukazatele).
Funkce m∙╛e vracet pouze jednu hodnotu. Pomocφ parametr∙ p°edßvan²ch
odkazem, m∙╛eme dosßhnout efektu nßvratu vφce hodnot. Funkce stßle vracφ
pouze jednu hodnotu, ale objekty p°edanΘ odkazem mohou b²t aktualizovßny
a funkce tak zdßnliv∞ vracφ vφce hodnot.
Dal╣φm d∙vodem pro p°edßvßnφ parametr∙ odkazem je eliminace re╛ijnφch
nßklad∙ spojen²ch s vytvß°enφm kopie objekt∙ p°i volßnφ funkce. P°i pou╛φvßnφ
standardnφch datov²ch typ∙ jsou re╛ijnφ nßklady na vytvo°enφ kopie zanedbatelnΘ.
P°i prßci se strukturami a t°φdami mohou b²t znaΦnΘ. Struktury v libovolnΘ
situaci lze p°edat odkazem (viz nßsledujφcφ ukßzka):
void nejakaFunkce(mojeStruktura&
s)
{
// ud∞lßme
n∞co se s
return;
}
mojeStruktura mojeStr;
....
nejakaFunkce(mojeStr);
Pou╛itφ odkazu umo╛≥uje modifikaci objektu p°edanΘho funkci. N∞kdy
se ale m∙╛eme dostat do situace, kdy tΘto modifikaci chceme zabrßnit.
-
Jestli╛e prom∞nnou deklarujeme s klφΦov²m slovem const, pak nem∙╛eme
zm∞nit jejφ hodnotu. StejnΘ °e╣enφ lze pou╛φt i p°i p°edßvßnφ parametru
funkcφ odkazem a ud∞lat tak konstantnφ objekt:
void nejakaFunkce(const
mojeStruktura& s)
{
// ud∞lßme
n∞co se s
return;
}
mojeStruktura mojeStr;
....
nejakaFunkce(mojeStr);
Nynφ m∙╛eme funkci p°edßvat objekt a nemusφme se obßvat, ╛e jej funkce
zm∞nφ. Pokus o modifikaci konstantnφho objektu uvnit° funkce zp∙sobφ p°i
p°ekladu chybu. Nap°.
void nejakaFunkce(const
mojeStruktura& s)
{
s.slozka =
100; // chyba, konstantnφ objekt nelze modifikovat
return;
}
Takov²to objekt je konstantnφ pouze uvnit° funkce. P°ed volßnφm funkce
a po nßvratu z funkce jej lze modifikovat (pokud nenφ p∙vodn∞ deklarovßn
jako konstantnφ).
Kdy╛ si mßme vybrat, zda parametr budeme p°edßvat odkazem nebo ukazatelem,
pak v∞t╣inou dßvßme p°ednost p°edßvßnφ odkazem. P°i p°edßvßnφ znakovΘho
pole je ale v²hodn∞j╣φ pou╛φt p°edßvßnφ parametru ukazatelem (ukazatel
na znakovΘ pole a jmΘno pole jsou zam∞nitelnΘ).
-
Zm∞≥te na╣i aplikaci s adresß°em na╣ich znßm²ch tak, ╛e u funkce zobrazZaznam
budete strukturu p°edßvat odkazem.
-
P°i vytvß°enφ a ru╣enφ dynamick²ch prom∞nn²ch se pou╛φvajφ operßtory new
a delete. S operßtorem new jsme se ji╛ seznßmili. K uvoln∞nφ
pam∞ti pou╛φvßme operßtor delete.
Jak ji╛ bylo uvedeno d°φve, pam∞╗ m∙╛eme alokovat lokßln∞ (v zßsobnφku)
nebo dynamicky (v hromad∞). Nßsledujφcφ ukßzka k≤du alokuje dv∞ pole. Jedno
je alokovßno v zßsobnφku (lokßlnφ alokace) a druhΘ v hromad∞ (dynamickß
alokace):
char buff[80];
char* velkyBuff =
new char[4096];
V prvnφm p°φpad∞ velikost alokovanΘ pam∞ti je malß a nenφ podstatnΘ,
zda bude pou╛it zßsobnφk nebo hromada. V druhΘm p°φpad∞ se jednß o velkΘ
pole a je tedy vhodnΘ jej alokovat v hromad∞ (╣et°φme mφstem v zßsobnφku).
V p°φpad∞ polφ (°et∞zec je pole typu char), dynamickΘ a lokßlnφ
instance jsou zam∞nitelnΘ. Tzn. pou╛φvajφ stejnou syntaxi:
strcpy(buff, "Ahoj");
strcpy(velkyBuff,
"Hodn∞ dlouh² °et∞zec ......");
// a pozd∞ji
strcpy(velkyBuff,
buff);
JmΘno pole bez operßtoru indexace ukazuje na zaΦßtek pole. Ukazatel
takΘ ukazuje na zaΦßtek pole a je tedy jedno zda pou╛ijeme jmΘno pole nebo
ukazatel na pole.
Pokud operßtor new nem∙╛e alokovat po╛adovanou pam∞╗, pak vracφ
NULL. To m∙╛eme testovat nap°. takto:
char* buff = new
char[1024];
if (buff) strcpy(buff,
"N∞jak² text");
else SignalizaceChyby();
Jestli╛e alokujeme velmi velkou oblast pam∞ti nebo alokujeme pam∞╗
v kritickΘ oblasti programu, pak je vhodnΘ testovat ukazatel na p°φpustnost
(·sp∞╣nost alokace). B∞╛nΘ alokace lze ponechat bez testovßnφ.
-
V╣echna alokovanß pam∞╗ musφ b²t dealokovßna (uvoln∞na), kdy╛ ji ji╛ nepot°ebujeme.
U lokßlnφch objekt∙ to probφhß automaticky. P°i pou╛itφ dynamickΘ alokace,
je za uvol≥ovßnφ zodpov∞dn² programßtor a k uvol≥ovßnφ pam∞ti pou╛φvß operßtor
delete.
V╣echna volßnφ new majφ svΘ odpovφdajφcφ delete. Operßtory
new a delete tvo°φ dvojici.
Pou╛itφ operßtoru delete je snadnΘ:
nejakyObjekt* mujObjekt
= new nejakyObjekt;
// n∞jakß Φinnost
s objektem
delete mujObjekt;
// objekt je zru╣en
P°i pou╛φvßnφ samotnΘho operßtoru delete nejsou ╛ßdnΘ problΘmy,
ale je n∞kolik v∞cφ spojen²ch s ukazateli a delete, na kterΘ musφme
dßvat pozor. Za prvΘ nesmφme zru╣it ukazatel (p°esn∞ji °eΦeno objekt na
kter² ukazatel ukazuje), kter² ji╛ byl zru╣en (vznikne chyba p°φstupu a
°ada dal╣φch problΘm∙). Za druhΘ m∙╛eme zru╣it ukazatel, kter² byl nastaven
na 0.
N∞kdy deklarujeme ukazatel pro p°φpad, kdy by mohl b²t pou╛it, ale
nevφme jist∞ zda v danΘ instanci na╣eho programu byl skuteΦn∞ pou╛it. Nap°.
m∙╛eme mφt objekt, kter² je vytvß°en, kdy╛ u╛ivatel provede jistou volbu
v nabφdce. Pokud u╛ivatel nikdy tuto volbu neprovede, pak objekt nebude
vytvo°en. ProblΘmem je, ╛e v p°φpad∞ vytvo°enφ objektu je zapot°ebφ zru╣it
ukazatel a neru╣it jej, kdy╛ objekt nebyl vytvo°en. Zru╣enφm neinicializovanΘho
ukazatele si zp∙sobφme problΘmy, proto╛e nevφme na kterou Φßst pam∞ti ukazuje.
Jak ji╛ bylo uvedeno d°φve je vhodnΘ inicializovat ukazatel nulou,
pokud jej p°φmo neinicializujeme normßlnφ hodnotou. To je ze dvou d∙vod∙
vhodn² zp∙sob. Prvnφ d∙vod byl popsßn v²╣e; neinicializovan² ukazatel obsahuje
nßhodnou hodnotu, co╛ je ne╛ßdoucφ. Druh²m d∙vodem je to, ╛e zru╣enφ ukazatele
NULL je p°φpustnΘ (m∙╛eme zru╣it tento ukazatel a nemusφme se zab²vat tφm,
zda jej u╛ivatel pou╛il):
ukazatel* nekdy =
0;
// mo╛nΘ pou╛itφ
nekdy
delete nekdy;
Zru╣enφ ukazatele v tomto p°φpad∞ je v po°ßdku a a╗ ji╛ ukazatel ukazuje
na objekt nebo je NULL.
M∙╛eme se takΘ dostat do situace, kdy vytvo°φme objekt v jednΘ Φßsti
programu a zru╣φme jej v jinΘ Φßsti programu. P°itom Φßst k≤du ru╣φcφ objekt
nemusφ b²t nikdy provedena. V tomto p°φpad∞ by bylo vhodnΘ zru╣it objekt
p°i ukonΦenφ programu (v n∞kter²ch p°φpadech ale objekt ji╛ mohl b²t zru╣en).
K zabrßn∞nφ dvojnßsobnΘho zru╣enφ je vhodnΘ ukazatel po zru╣enφ nastavit
na NULL (nebo 0):
ukazatel* vytvoren
= new ukazatel;
// pozd∞ji
delete vytvoren;
vytvoren = 0;
Pokud nynφ pou╛ijeme na tento objekt dvakrßt operßtor delete,
pak se nic nestane, nebo╗ nenφ chybou zru╣it ukazatel NULL.
Jinou mo╛nostφ, jak zabrßnit problΘmu dvojnßsobnΘho zru╣enφ je testovßnφ
ukazatele na nenulovou hodnotu p°ed pou╛itφm delete:
if (nekdy) delete
nekdy;
To ale takΘ p°edpoklßdß nastavenφ ukazatele na 0 p°i jeho zru╣enφ.
Pokud p°i dynamickΘm vytvo°enφ objektu pou╛ijeme odkaz, pak syntaxe
ru╣enφ je jinß. Nßsledujφcφ p°φklad ukazuje tento problΘm:
mojeStruktura&
odkaz = *new mojeStruktura;
odkaz.X = 100;
delete &odkaz;
Vidφme, ╛e ru╣enφ ukazatele v p°φpad∞ odkazu vy╛aduje pou╛φt adresov²
operßtor. Odkaz nelze nastavit na 0 a tedy musφme b²t opatrnφ a neru╣it
odkaz dvakrßt.
-
Kdy╛ se vrßtφme k na╣emu programu adresß°e na╣ich znßm²ch, tak vidφme,
╛e program je chybn² (ve verzi, kde pracujeme s ukazateli a verzi s odkazy).
Dynamicky alokovanß pam∞╗ nenφ uvol≥ovßna. Vytvo°enΘ pole struktur alokovan²ch
v hromad∞ nenφ nikdy uvoln∞no z pam∞ti. Na konec na╣eho programu (za °ßdek
getch();)
je nutno vlo╛it:
for (int i = 0; i
< 3; i++)
delete seznam[i];
Nynφ ji╛ mßme sprßn² program. Projdeme pole ukazatel∙ a zru╣φme ka╛d²
z nich samostatn∞.
-
Kdy╛ volßme new k vytvo°enφ pole, pak m∙╛eme pou╛φt verzi new[]
tohoto operßtoru. Nenφ d∙le╛itΘ znßt jak toto pracuje, ale je nutno v∞d∞t
jak probφhß ru╣enφ dynamicky alokovan²ch polφ. Pou╛ijeme jednu z p°edchozφch
ukßzek, na jejφ╛ konec p°idßme p°φkaz delete[]:
char buff[80];
char* velkyBuff =
new char[4096];
strcpy(buff, "Ahoj");
strcpy(velkyBuff,
"Hodn∞ dlouh² °et∞zec ......");
// a pozd∞ji
delete[] velkyBuff;
Je zde pou╛it operßtor delete[]. Nebudeme se zde zab²vat technick²m
popisem, ale m∙╛eme si b²t jisti, ╛e v╣echny prvky pole jsou zru╣eny.
-
Nßsledujφcφ konzolovß aplikace deklaruje dvourozm∞rnΘ pole dynamicky. V
na╣em p°φpad∞ mß pole t°i °ßdky a p∞t sloupc∙, ale jeho rozm∞ry m∙╛eme
snadno modifikovat. Dvourozm∞rnΘ pole je tvo°eno jednorozm∞rn²m polem s
ukazateli na jednotlivΘ °ßdky, tj. s ukazateli op∞t na jednorozm∞rnß pole.
#include <iostream.h>
#include <conio.h>
void display(long
double **);
void de_allocate(long
double **);
int m = 3;
// PoΦet °ßdk∙.
int n = 5;
// PoΦet sloupc∙.
int main(int argc,
char **argv) {
long
double **data;
data
= new long double*[m];
// Krok 1: Nastavenφ °ßdk∙.
for
(int j = 0; j < m; j++)
data[j] = new long double[n]; // Krok 2:
Nastavenφ sloupc∙
for
(int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
data[i][j] = i + j;
// Inicializace pole
display(data);
de_allocate(data);
getch();
return
0;
}
void display(long
double **data) {
for
(int i = 0; i < m; i++) {
for (int j = 0; j < n; j++)
cout << data[i][j] << " ";
cout << "\n" << endl;
}
}
void de_allocate(long
double **data) {
for
(int i = 0; i < m; i++)
delete[] data[i];
// Krok 1: Zru╣enφ sloupc∙
delete[]
data;
// Krok 2: Zru╣enφ °ßdk∙
}
Sna╛te se pochopit, jak tento program pracuje. Zm∞≥te tento program
tak, aby rozm∞ry pole bylo mo╛no zadat a╛ p°i spu╣t∞nφ programu.
-
HlaviΦkov² soubor ctype.h obsahuje °adu funkcφ testujφcφch znaky
(isalnum, isalpha, isdigit, islower a dal╣φ)
a funkcφ p°evßd∞jφcφch znaky (toascii, tolower, toupper,
atd.). Seznamte se s jejich pou╛φvßnφm (pomocφ nßpov∞dy).
Zßkladnφ pravidla pro pou╛φvßnφ ukazatel∙ a dynamickΘ alokace pam∞ti:
-
Inicializujte ukazatele nulou, pokud jim p°φmo nep°i°azujete hodnotu.
-
Zabra≥te dvojnßsobnΘmu zru╣enφ ukazatel∙.
-
Nenφ chybou ru╣it ukazatele nastavenΘ na NULL nebo 0.
-
Po zru╣enφ ukazatele jej nastavte na NULL nebo 0.
-
Dereferencφ ukazatel∙ zφskßme objekt, na kter² ukazatel ukazuje.