Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
C/C++
Použití čítače referencí v C++
28. března 2002, 00.00 | Dnes si ukážeme jak použít náš jednoduchý "garbage collector" v C++. Minule jsme pro tyto účely vytvořili šablonu Pointer. Dnes si ukážeme jak ji používat.
V minulém článku Inteligentní ukazatel - čítač referencí v C++ jsme si implementovali šablonu Pointer. Jedná se o "inteligentní" ukazatel, který se odkazuje na objekt s čítačem referencí. Není-li na objekt žádná reference, objekt bude automaticky zlikvidován. Dnes si ukážeme jak s náš Pointer používat.
Nejprve si vytvoříme jednoduchou třídu. Instance této třídy budeme používat v následujících příkladech.
|
Nyní vytvoříme "inteligentní" ukazatel na instanci třídy Třída. Parametr šablony je typ prvku, na který "inteligentní" ukazatel ukazuje. Parametrem konstruktoru může být jednak jiný "inteligentní" ukazatel (kopírovací konstruktor), nebo normální ukazatel na vytvořený objekt. Všimněte si také na příkladu, že lze použít i operátory == a !=. V příkladech nesmíme zapomenout, že Pointer je deklarován v souboru pointer.h v prostoru jmen www_builder_cz.
|
K nastavení ukazatele na NULL slouží metoda setNULL(). Předáme-li jednou našemu "inteligentnímu" ukazateli normální ukazatel, je dobré odstranit všechny normální ukazatele na tento objekt. Inteligentní ukazatelé jej totiž zlikvidují až se jim to bude zdát vhodné. Na normální ukazatele neberou ohled. Buď budeme s ukazatelem pracovat pomocí normálních ukazatelů nebo pomocí šablony Pointer. Kombinovat obě možnosti je velmi nebezpečné.
Inteligentní ukazatel jako parametr funkce, či metodyInstance šablony Pointer může být bez problému použita jako typ parametru funkce.
|
Možná někoho od používání Pointer odradí fakt, že má k dispozici již mnoho funkcí, či metod, které jako své parametry vyžadují normální ukazatele. I tento problém lze řešit. Zde ale již musíme dávat trochu pozor. Začínáme totiž kombinovat přístup k objektu pomocí normálních a našich "inteligentních" ukazatelů. Předání skutečného ukazatele na objekt jako parametru funkce či metody by to nevadilo pokud:
- Ve funkci či metodě nedojde k dealokaci objektu, na který se odkazuje ukazatel předán jako parametr. Důvod je jasný. Funkce, nebo metoda by zničila objekt, se kterým by "inteligentní" ukazatel dále pracoval.
- Funkce, nebo metoda neudělá s ukazatelem nějaký "vedlejší efekt". Například nezapíše ukazatel na náš objekt jako globální proměnnou, nebo jako atribut objektu, jedná-li se o metodu. Vznikl by tím odkaz na něco, co může být kdykoliv dealokováno.
Představme si například, že máme nějakou funkci void f(Trida *t);. Je-li ptr typu Pointer<Trida>, lze funkci volat f(&*ptr);. Tím získáme skutečný (normální) ukazatel na objekt třídy Třída.
Funkce a metody nezávislé na typu ukazateleExistuje také možnost vytvořit funkce či metody nezávislé na tom, zda se jako parametr použije normální, nebo "inteligentní" ukazatel. jak asi každého napadne, jedná se o šablony, jejichž parametrem je typ ukazatele. Příklad:
|
Použití:
|
Přetypování
Problémy s přetypováním jsem nastínil ve svém předchozím článku. Dnes si ukážeme jak metodu cast použít. Znovu jen zopakuji, že cast je naprosto nepoužitelná pro přetypování "inteligentního" ukazatele na primitivní datový typ. Také není vhodná na přetypování instancí třídy vzniklých vícenásobnou dědičností. Metoda cast je vnořená šablona. Typ této šablony je novým typem, na který chceme přetypovat. Ukážeme si vše na příkladu.
|
Šablona pointer není žádný garbage collector, jaký známe z jiných programovacích jazyků (např. Java). Není to dokonce ani náhrada za garbage collector. Když jsem se v upoutávce na článek o garbage collectoru zmínil, jednalo se spíše o nadsázku. Jak jsem již v minulém článku upozorňoval čítač referencí je bezmocný na kruhové vazby mezi objekty. Máme-li například objekty A,B. V A je ukazatel ukazující na B, v B je ukazatel ukazující na A. Nebude-li existovat žádný jiný ukazatel na A, nebo B, objekty by měly být oba zničeny. Nic takového se ale nestane, protože každý z objektů bude mít čítač referencí nastaven na 1. Takové "kruhy" musíme sami "přetrhnout". Například metodou setNULL, kterou zavoláme pro jeden z ukazatelů tvořících kruh. Tento problém by pro skutečné GC například z Javy nebyl žádným problémem. Skutečné GC totiž pracují trochu jinak.
ShrnutíŠablonu Pointer rozhodně doporučuji k prostudování, ale při praktickém použití v programech má nějaké ty nevýhody. Pokud o nich ale víme, a nepřekvapí nás, myslím že je možné "inteligentní" ukazatele používat. Zvlášť za velkou nevýhodu považuji problémy s přetypováním. Ty z Vás, kteří nečtou mé články pravidelně, asi překvapilo, co to vlastně ten identifikátor Pointer je. Jedná se o šablonu, která je ke stažení zde. Šablonou Pointer jsem se pokusil vylepšit standardní čablonu auto_ptr.
Tímto téma čítače odkazů ukončíme. Příště se podíváme na problém, který s počítáním odkazů jen trochu souvisí. Podíváme se na potlačení kopírování velkých objektů. Představme si, že předáváme jako parametr funkce či metody velký objekt. Tento parametr je nutné zkopírovat, což je u velkých objektů neefektivní. Z nějakých důvodů ale nechceme předávat pouze referenci, nebo ukazatel. Jak to řešit? Uvidíme příště.
Obsah seriálu (více o seriálu):
- Základy OOP v C++: Od C k C++
- Základní pojmy objektově orientovaného programování
- Vytváření tříd, instance třídy, zasílání zpráv v C++
- Vytváření instancí - konstruktory, destruktory
- Kopírovací konstruktor v C++
- Jednoduchá dědičnost v C++
- Časná versus pozdní vazba - úvod do polymorfismu v C++
- Polymorfismus - dokončení
- Vícenásobná dědičnost v C++
- Vícenásobná dědičnost v C++ - opakovaná dědičnost
- Vícenásobná dědičnost v C++ - volání konstruktorů a destruktorů
- Přetěžování operátorů v C++ 1.díl
- Přetěžování operátorů v C++ 2. díl
- Vstupní a výstupní operace pomocí datových proudů v C++
- Přetěžování operátorů << a >> pro datové proudy v C++
- Neformátovaný vstup a výstup v C++
- Paměťové proudy v C++
- Prostory jmen v C++
- Řetězce v C++
- Výjimky v C++
- Výjimky v C++ - výjimky tvoří dědičnou hierarchii
- Výjimky v C++ - dokončení
- Dynamická identifikace typů v C++
- Přetypování v C++
- Problémy s typy při vícenásobné dědičnosti
- Šablony funkcí v C++
- Šablony datových typů v C++
- Vnitřní typy u parametrů šablon, vnořené šablony v C++
- Pole s libovolným intervalem indexování v C++
- Datové kontejnery v C++ - Úvod do STL
- Vector - datový kontejner v C++
- Iterátory v C++
- Šablona vector v C++ a iterátory
- Asociativní pole v C++
- Množina v C++
- Funkční objekty v C++
- Standardní funkční objekty v C++
- Úvod do standardních algoritmů v C++
- Kopírovací a přesouvací algoritmy v C++
- Vyhledávací algoritmy v C++
- Skenovací (prohlížecí) algoritmy v C++
- Transformační algoritmy v C++
- Řadící algoritmy v C++
- Halda v C++
- Standardní algoritmy v C++ - dokončení
- Automatické ukazatele v C++
- Inteligentní ukazatel - čítač referencí v C++
- Použití čítače referencí v C++
- Kopírování velkých objektů v C++
- Řízené kopírování prvků v poli v C++
- Dokončení seriálu objektově orientované programování v C++
Diskuse k článku
-
25. listopadu 2012
-
30. srpna 2002
-
10. října 2002
-
4. listopadu 2002
-
12. září 2002
-
25. listopadu 2012
-
28. července 1998
-
31. července 1998
-
28. srpna 1998
-
6. prosince 2000
-
27. prosince 2007
-
4. května 2007