× Aktuálně z oboru

Vychází Game Ready ovladače pro Far Cry 5 [ clanek/2018040603-vychazi-game-ready-ovladace-pro-far-cry-5/ ]
Celá zprávička [ clanek/2018040603-vychazi-game-ready-ovladace-pro-far-cry-5/ ]

C++ - 10. lekce

[ http://programujte.com/profil/1483-lukas-chury/ ]Google [ https://plus.google.com/101446994797551111026?rel=author ]       [ http://programujte.com/profil/118-zdenek-lehocky/ ]Google [ ?rel=author ]       16. 7. 2005       59 825×

  • alokace paměti pomocí operátoru new
  • uvolnění paměti pomocí operátoru delete
  • upozornění na práci s pamětí
  • úkol č.10

Nyní byste měli mít jistý pocit, jak ukazatelé pracují. Ovšem pojďme dále - alokování paměti za běhu programu.

Doposud jste inicializovali proměnné adresami proměnných. Co to jsou tedy proměnné? Pojďme si to zopakovat. Proměnné jsou pojmenovanou pamětí alokovanou v době kompilace. Ukazatele pouze poskytují druhé jméno pro paměť, ke které byste mohli přistupovat podle jména. K čemu tedy ukazatele? Pravá cena ukazatelů vyplouvá na povrch, když alokujete nepojmenovanou paměť na úschovu hodnot v době běhu programu. V tomto případě se k paměti dostanete pouze pomocí ukzatelů. Pokud jste se dříve učili C, mohli jste paměť alokovat pomocí f-ce malloc() - to stále můžete, ale C++ má lepší způsob - operátor new

Alokace paměti pomocí operátoru new

Takže jak na to?
Zkusíme si vytvořit nepojmenovanou paměť za běhu programu pro hodnotu typu int a budeme k ní přistupovat pomocí ukzatele. Poskytneme operátoru new typ dat, pro kterou chceme paměť, new nalezne blok správné velikosti a navrátí jeho adresu, tu přiřadíme ukazateli.

int * vek = new int

Tento příkaz říká programu, že chceme novou paměť vhodnou pro úschovu int. Jakmile ji nalezne, vrátí jeho adresu a ta se přiřadí do vek - což je typ ukazatele na int. Nyní je tedy vek adresou a * vek hodnotou uložené na dané adrese. Ukazatele se vám možná jeví nešikovně, ale máte větší pohled nad tím, jak váš program spravuje paměť. Ukažme si tedy obecný tvar:

jmeno_typu jmeno_ukazatele = new jmeno_typu

Poprvé určujete datový typ (int) požadované paměti a podruhé deklaraci vhodného ukazatele.


#include <iostream>
using namespace std;
int main()
{
  int * promenna = new int; // alokuje prostor pro int
  *promenna = 1001; // uloží tam hodnotu
  cout << "int hodnota = " << *promenna << ": umisteni = "
  << promenna << "
";
  double * promenna2 = new double; // alokuje prostor pro double
  *promenna2 = 10000001.0; // uloží tam double
  cout << "double hodnota = " << *promenna2 << ": umisteni = " 
  << promenna2 << "
";
  cout << "velikost promenna = " << sizeof promenna;
  cout << ": velikost *promenna = " << sizeof *promenna << "
";
  cout << "velikost promenna2 = " << sizeof promenna2;
  cout << ": velikost *promenna2 = " << sizeof *promenna2 << "
";
  cin.get();
  return 0;
}
A výsledek :

int hodnota = 1001: umisteni = 00420750
double hodnota = 1e+007: umisteni = 00420710
velikost promenna = 4: velikost *promenna = 4
velikost promenna2 = 4: velikost *promenna2 = 8

Ukazatele promenna a promenna2 ukazují na dva datové objekty - bez nich nemůžete k tmto paměťovým lokacím přistupovat. Zde také vidíme, proč musíme deklarovat typ, na který ukazatel ukazuje. Adresa odhalí jen začátek, kde je objekt uložen - jak vidíte, velikost ukazatele na int i double je stejná. Ale protože jsme deklarovali typy ukazatelů, program ví, že promenna2 má 8 bajtů.

Mimo rozsah paměti?
Pokud počítač nemá dostatek volné paměti, vrací hodnotu 0* => prázdný ukazatel. C++ zaručuje, že prázdný ukazatel nikdy neukazuje na platná data, takže se často používá na indikování selhání operátorů či f-cí, které jinak navracejí nepoužitelné ukazatele.

* pan Mirek Virius doplňuje:
Podle standardu ISO 14882-2003 operátor new v základní podobě nevrací při neúspěchu 0, ale vyvolává vyjímku ntypu std::bad_alloc. Chceme-li, aby vracel 0, musíme použít podobu new(nothrow) (a předtím uvést #include).

Možná stále nerozumíte - "proč bych měl používat něco, co je zbytečné, těžké... proč bych měl pracovat s nepojmenovanou pamětí, když můžu jednoduše pracovat s proměnnou?"
Odpověď je jednoduchá - klasické proměnné alokují paměť v době kompilace, kdežto alokovaná paměť pomocí operátoru new se alokuje ZA běhu programu - tedy pouze tehdy, pokud bude využita, potřebná... Klasická proměnná se vždy "přihlásí" o své místo na začátku běhu programu. Ale co když nepotřebujete tolik proměnných? Co když se ukáže, že některé proměnné nebudou za celý běh programu ani jednou využity? Myslíte si, že to není možné? A co podmínky? Jestliže uživatel zadá ano, provede se určená část programu. Takže možnost "ne" zůstane neupotřebena a veškerý následující kód po této podmínce nebude upotřeben - ani proměnné. Tedy zbytečně jste plýtvali místem. Kdežto při alokaci paměti během programu se vám toto nestane - paměť se alokuje pouze tehdy, je-li to nutné. Možná si říkáte, proměnné jsou jednodušší..., ale vy si nemůžete poté u složitějších programů plýtvat pamětí!! Určitě vás u nových her štve, že mají takové hardwarové nároky. Takže neučte se zavrhovat a odsuzovat alokoci paměti za běhu programu, naopak, naučte se to používat. No a proč tedy ukazatele? No přeci proto, abyste nějak mohli s danou pamětí pracovat. Tedy:
Pojmenovaná paměť - int a - můžeme se na ní odkázat jménem proměnné nebo ukazatelem na proměnnou.
Nepojmenovaná paměť - int vek = new int - můžeme se na ni odkázat pouze ukazatelem

Uvolnění paměti pomocí operátoru delete

Mysleli jste si, že alokujete paměť za běhu programu a konec? Ne, tak jednoduché to není. Klasická proměnná je po ukončení programu zničena (uvolněna), ale paměť alokovaná za běhu programu zůstává i nadále po ukončení progrmu alokována, není uvolněna!

Pamatujte, takto alokovanou paměť (pomocí operátoru new) VŽDY uvolněte pomocí operátoru delete!

Takto uvolněná paměť může být poté znova použita jinou částí vašeho programu. Jedná se o nejefektivnější využití paměti.


int * promenna = new int;   //alokace paměti pomocí new
delete promenna;   //uvolnění paměti pomocí delete

Toto odstraňuje paměť, na kterou ukazuje promenna, neodstraňuje to samotný ukazatel. Můžete ho znovu použít, aby ukazoval na jinou lokaci. Dále byste se neměli pokoušet uvolňovat blok paměti, který jste již uvolnili - výsledek není definován.


int * promenna = new int;   //ok
delete promenna;   //ok
delete promenna;   //chyba - již uvolněná paměť
int vek = 5;   //ok
int * cil = & vek;   //ok
delete cil;   //chyba - paměť nebyla alokována pomocí new

Upozornění:
Používejte delete pouze k uvolnění paměti alokované pomocí new a zapamatujte si, že je bezpečné aplikovat delete na nulový ukazatel.

Rozhodujícím testem na použití delete je to, že ho použijete na paměť alokovanou pomocí new. To znamená, že nepotřebujete použít stejný ukazatel k uvolnění paměti, který jste použili u new:


int * promenna = new int;   //alokuje paměť
int * prom = promenna;   //nastavuje druhý ukazatel na stejný blok
delete prom;   //uvolníte paměť pomocí druhého ukazatele

Obyčejně dva ukazatele na stejný blok paměti nechcete vytvořit, protože tím vzrůstá pravděpodobnost, že se chybně pokusíte uvolnit stejný blok 2×. Ale jak brzy uvidíte, použití druhého ukazatele má smysl, když pracujete s f-cí, která navrací ukazatel.

Úkol č.10

Bude lehký - alokujte 2× int, 1× double a 1× float(ne jako proměnné, ale pomocí operátoru new), nechte do nich uživatele zadat hodnoty a poté se ho zeptejte, zda bude potřebovat danou paměť, pokud ne, paměť se uvolní, pokud ano, nabídnete mu přepsání stávajících hodnot. A tak dokola, dokud uživatel neuzná data za zbytečná a dá uvolnit. Jakmile dá uvolnit, vypíšou se napsaná čísla. Budete tedy muset čísla nakopírovat do klasických proměnných.

V příští lekci se naučíte vytvářet dynamická pole pomocí operátoru new a jejich použití.


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2005072302-c-10-lekce/ ].