Procvičení neobjektových vlastností jazyka C++ 2
Pokračujte v minulém cvičení procvičení neobjektových vlastností.
U následujících bodů se předpokládá, že po implementaci funkcí/vlastností tyto použijete při volání z funkce main a zjistíte, zda-li korektně fungují. Proto je vhodné „potrápit“ funkci několika voláními (s malými čísly, velkými čísly, zápornými čísli, nulovými hodnotami a jejich kombinacemi).
Nezapomeňte na kontrolu hlášení funkce check – manipulace s pamětí musí být v pořádku.
Reference
10) Napište funkci vykonávající stejnou činnost jako AllocVector(TVector*, int), ale pro předání proměnné struktury použijte reference AllocVector(TVector &, int). Nechte v programu obě funkce. Ověřte, že je to možné, a že obě fungují. Vysvětlete rozdíly.
11) Podobně převeďte předávání parametrů do a z (ostatních) funkcí
na předávání pomocí reference (tam kde to jde a dává to smysl).
Předávání pomocí reference je výhodné tam, kde předáváme větší
proměnné (složené proměnné struct, class takřka vždy, pokud z
předávání pomocí hodnoty neplynou nějaké výhody). Dále je reference
výhodná tam, kdy chceme proměnnou ve funkci změnit tak, aby se změna
projevila i mimo funkci.
Konstantní parametry
12) Projděte prototypy funkcí a definice jejich parametrů. Parametry, které se v dané části programu (funkci) nemění, označte jako const. Změnu je nutné provést ve zdrojovém (u definice funkce) i v hlavičkovém souboru (u deklarace funkce).
Označování proměnných pomocí const je z programátorského hlediska důležitá dovednost. Pokud budeme důsledně dodržovat pravidlo, že proměnné, které se ve funkci neměnní jsou v hlavičce označeny const a proměnné bez tohoto kvalifikátoru se mění, usnadní nám toto velice orientaci v kódu. Pouze podle hlavičky funkce se můžeme orientovat v tom, které proměnné se ve funkci změní, a které zůstanou stejné a nemusíme „luštit“ funkce, nebo číst jejich komentáře (pokud je autor vůbec uvedl).
Rozdělení do souborů
13) Pro další práci s vytvářenými funkcemi je výhodné, aby byly ve
zvláštním zdrojovém souboru (ty funkce, které k sobě logicky náleží)
a příslušný hlavičkový soubor. Proto projekt rozdělíme na tak aby s
funkce main byla v jednom zdrojovém souboru a sloužila jako ukázka
použití funkcí a zároveň jako jejich testovací kód. Pro funkce je
vhodné mít jejich kód ve zdrojovém souboru a k němu hlavičkový
soubor, který tvoří interface – nebo-li uvede proměnné, které
je možné používat v ostatních modulech programu.
Vytvořte soubory
funkce.cpp a funkce.h. Hlavičkový soubor standardně ošetřete a
přesuňte do něj:
- definici struktury
- hlavičky funkcí
-
definice konstant a proměnných, které je nutné sdílet v rámci
programu s ostatními moduly.
Do zdrojového souboru funkce.cpp
přesuňte funkce, které souvisí s prací se strukturami (funkce main
zůstane v původním souboru.)
Hlavičkový soubor přidejte (includujte) do zdrojových souborů (s main i se zdrojovými kódy knihovny pro práci s TVector).
Vytvoření struktury pro 2D pole
14) Vytvořte strukturu TPoleVect, která bude obsahovat 2D pole – v našem případě bude tedy obsahovat pole struktur typu TVector. Návrh by měl být podobný jako u TVector (naalokovaná délka, využitá délka a vlastní data – pole TVector).
15) Napište funkci, která vztvoří strukturu TPoleVect
platnými daty (naalokuje místo pro struktury i jejich vnitřní prvky –
pole hodnot)
AllocPoleVectoru(TPoleVect & aPole, size_t
aPocetVektoru, size_t aPocetPrvkuVektoru,XX
aInicializacniHodnota).
Název funkce je stejný jako již dříve
vytvořená jiná funkce, pouze parametry jsou jiné. K chybě při
překladu nedojde díky mechanizmu přetěžévání funkcí – v C++
může mít více funkcí stejný název pokud se jednoznačně liší parametry
funkce ().
16) Napište funkci, která uvolní prvky uvnitř struktury (nejdříve pole hodnot a potom pole struktur TVector).
Funkce pro nastavení a získání hodnoty
17) Vytvořte funkce pro zadávání a čtení hodnoty z pole –
tzv. gettery a settery.
Tyto funkce slouží jako mezičlánek mezi
„běžným“ uživatelem a vlastní třídou. Tato koncepce
návrhu, kdy se k proměnným nepřistupuje přímo, ale pomocí funkcí,
umožní kontrolovat přístup do pole – funkce zkontroluje
požadované parametry (zde řádku a sloupce) a pokud tyto v poli
neexistují, vygeneruje výjimku. Tyto funkce budou mít jako parametr
strukturu TPoleVect a pozice prvku, se kterým se má pracovat.
Funkce provede kontrolu, zda pozice je uvnitř pole – pokud
není, zahlásí chybu pomocí výjimky.
Pokud je prvek uvnitř pole
možné vrátit referencí, můžeme jako getter a setter použít jedinou
funkci. Výhodou této koncepce je jednoduchost a to, že se předávají
reference (úsporné předávání parametrů). Nevýhodou je to, že nyní
nedošlo k úplnému oddělení dat, protože přes referenci je možné
zjistit adresy prvků pole a následně ho „napadnout“.
K
úplnému oddělení dat by došlo, kdyby byly funkce dvě a getter vracel
hodnotu (nebo alespoň referenci na konstantní proměnnou).
17a) Napište funkce XX Get(TPoleVect, index1, index2) a Set(TPoleVect, index1, index2, value) , kde XX je typ vkládané nebo získávané proměnné. Tyto funkce pracují s hodnotami. Uživatel nemá přístup k hodnotám pole.
17b) Jelikož nejčastěji nastavovanou hodnotou je nula, využijte pro funkci Set mechanizmus implicitních parametrů pro nastavení hodnoty value na nulu.
17c) Funkce s přímým přístupem do pole je pohodlnější, stačí
jedna, ale je nebezpečnější v tom, že uživatel získá přímý přístup k
prvkům – a může je tedy měnit bez kontroly funkcí interface.
XX& GetRef(TPoleVect, index1, index2)
s
použitím pro získání hodnoty
x = GetRef(TPoleVect,
index1, index2);
a s použitím pro nastavení hodnoty
GetRef(TPoleVect, index1, index2) = 10;
Oddělení dat
18) Aby se nevytvářely kolize mezi kódy programátorů, kteří na
programu spolupracují, je výhodné použít mechanizmus jmenných
prostorů (NameSpace).
Vyzkoušejte umístění všech vytvořených
datových struktur a funkcí do jmenného prostoru NSFunkce (v
hlavičkovém i zdrojovém souboru funkcí).
19) Upravte kód s funkcí main tak, aby akceptoval změnu
zařazení funkcí a struktur do jmenného prostoru.
- Začněte
nejjednodušším způsobem using namespace NSFunkce – toto
řešení je jednoduché ale zveřejní celý jmenný prostor a tím popře
jeho hlavní funkci – oddělit použitá jména od ostatních.
-
Použijte zveřejnění vybraných prvků (proměnné, funkce, hodnoty)
pomocí using NSFunkce::funkce ale u některých si zkuste i
přímý přístup v kódu pomocí celého jména (název prostoru :: název
funkce) NSFunkce::AllocPole()
Poslední úpravy 2017-10-09