Rozdíly

Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.

Odkaz na výstup diff

public:pb161:fall14_cviko10 [2018/02/24 19:10] (aktuální)
Řádek 1: Řádek 1:
 +====== Cvičení 10 ======
 +
 +===== Úvod do cvičení =====
 +Na tomto cvičení si budete trochu hrát s typovým systémem jazyka C++. Uvidíte, jaké informace o typu objektu můžete za běhu zjistit, a jak dynamicky změnit typ objektu z předka na potomka. Navážete na úlohy z minulého cvičení a opět budete pracovat s objekty //Circle//, //​Rectangle//​ a //​ObjectSet//​. V případě, že z nějakého důvodu nemáte úlohy z minulého cvičení dokončené,​ můžete si {{:​public:​pb161:​fall14_cviko09_reseni.zip|zde}} stáhnout vypracované řešení.
 +===== První úkol =====
 +Napište metodu ''​objectTypes''​ třídy //​ObjectSet//,​ která vypíše typy objektů, které se v ní nachází. Tuto věc lze napsat i pomocí virtuálních metod, nicméně v tomto úkolu se držte použití operátoru ''​typeid''​.
 +
 +==== Zadání úkolu ====
 +  * Hlavička metody by měla vypadat nějak takto: ''​void ObjectSet::​objectTypes(std::​ostream&​ out);'', ​
 +  * Výpis by mohl vypadat například takto: Circle Circle Rectangle Circle...
 +  * Název třídy můžete získat metodou ''​std::​type_info::​name()''​.
 +
 +=== Poznámky ===
 +  * Operátor ''​typeid''​ můžete použít až poté, co přiložíte hlavičkový soubor ''<​typeinfo>''​.
 +  * Třída ''​std::​type_info''​ obsahuje informace o typu, nicméně tyto informace mohou být různé pro stejný typ ve stejném programu v rámci různých běhů.
 +    * Ovšem stejné v případě jednoho běhu bude informace o typu stejná.
 +    * Textový název třídy může být zavádějící -- může se lišit od názvu, který jste napsali vy.
 +
 +===== Druhý úkol =====
 +Vytvořte metodu ''​doubleObject''​ ve třídě //​ObjectSet//,​ která zvětší všechny objekty v množině. Tuto metodu implementujte bez zásahu do tříd //Circle// a //​Rectangle//​.
 +
 +==== Zadání úkolu ====
 +  * Metoda ''​doubleObjects''​ provede následující činnost
 +    * Každému kruhu zdvojnásobí poloměr.
 +    * Každému obdélníku zdvojnásobí šířku a výšku.
 +  * Pro zjištění jakého typu je jaký objekt využijte operátor ''​dynamic_cast''​.
 +    * Použitím se můžete inspirovat v metodě ''​equals''​.
 +
 +
 +
 +
 +===== Třetí úkol =====
 +Zatím používáme pro uložení prvků množiny kontejner ''​std::​vector''​. Tento kontejner ale není vhodný pro reprezentaci množiny, mnohem vhodnější je použít kontejner ''​std::​set'',​ nicméně tato změna s sebou přináší netriviální změnu kódu. Proto je tento úkol rozdělen na tři na sebe navazující kroky.
 +
 +Prvně si musíme uvědomit, jak se doposud přistupovalo k prvkům množiny -- postupně se procházel ''​std::​vector''​ a zkoušela se přesná shoda pomocí metody ''​equals''​. Přístup k prvkům v kontejneru ''​std::​set''​ je ale odlišný -- ustanoví se pořadí na prvcích. V případě, že bychom neukládali ukazatel na //​Geometry2D//,​ tak by bylo možné definovat ''​operator<''​. Takto si ale musíme vytvořit jiný způsob.
 +
 +==== Krok 1 - Vytvoření popisujícího vektoru ====
 +
 +Pro to, abychom mohli jednoznačně určit pořadí na jednotlivých prvcích, si můžeme například vytvořit popisující vektor, který následně bude možné jednoznačně porovnat. Na základě tohoto vektoru bude posléze možné od sebe odlišit nejen //Circle// od //​Rectangle//,​ ale také jednotlivé -- různé -- instance od sebe. Na základě typu položek ve třídách //Circle// a //​Rectangle//​ se jako nejvhodnější reprezentace jeví použít třídu ''​std::​vector''​ a jako položky použít číslo (''​int''​ nebo ''​unsigned int''​).
 +
 +=== Formát popisujícího vektoru ===
 +
 +Každý potomek třídy //​Geometry2D//​ bude mít jistě nejméně takovýto vektor:
 +
 +^ Id ^ Hodnota ^
 +| 0 | Hash názvu třídy |
 +| 1 | pozice X |
 +| 2 | pozice Y |
 +| 3 | barva |
 +
 +Další položky do popisujícího vektoru si poté doplní třídy podle toho, jaké mají atributy -- průměr, výšku, šířku, ...
 +
 +<note tip>
 +Jak vytvořit hash názvu třídy?
 +Ti z vás, kteří používají C++11, můžete využít metody ''​std::​type_info::​hash_code'',​ která vám rovnou vrátí hash názvu třídy.
 +
 +Vy ostatní si musíte napsat generování hashe sami. Můžete se inspirovat tímto fragmentem kódu (který je převzaný z implementace metody ''​std::​type_info::​hash_code''​ v libc++):
 +<code cpp>
 +const char *ptr = typeid(*this).name();​
 +unsigned hash = 5381;
 +while (unsigned char c = static_cast<​unsigned char>​(*ptr++))
 +    hash = (hash * 33) ^ c;
 +</​code>​
 +</​note>​
 +
 +=== Jak a kde generovat popisující vektor ===
 +
 +Jedna z možností je následující:​
 +  - Ve třídě //​Geometry2D//​ vytvoříte virtuální metodu s nějakým názvem (vzorové řešení používá název ''​properties''​) a s následující signaturou:
 +    * <code cpp>​virtual std::​vector<​unsigned>​ properties() const;</​code>​
 +  - V této metodě implementujete vytvoření základního vektoru se čtyřmi položkami.
 +  - V potomcích třídy //​Geometry2D//​ překryjete původní metodu ''​properties''​.
 +    - Necháte si vytvořit vektor zavoláním metody z předka
 +      * <code cpp>​std::​vector<​unsigned>​ prop = Geometry2D::​properties();</​code>​
 +    - Přidáte do vektoru další vlastnosti, které popisují třídu.
 +    - Vrátíte vytvořený vektor
 +
 +
 +==== Krok 2 - Vytvoření komparátoru ====
 +
 +Druhý šablonový parametr třídy ''​std::​set''​ je komparátor. Třída ''​std::​set''​ v zásadě požaduje, aby komparátor splňoval následující:​
 +
 +  * Je to třída, která definuje uspořádání na prvcích množiny.
 +  * Má implementovaný operátor volání -- ''​operator()''​ -- pro který platí:
 +    * Je schopný akceptovat dva parametry stejného typu, jaký je uložen v ''​std::​set''​.
 +    * Své parametry nesmí nijak modifikovat.
 +      * V našem případě tedy parametry budou typu ''​const Geometry2D *''​.
 +    * Nesmí modifikovat ani komparátor.
 +    * Vrací ''​true''​ pokud má být první parametr před druhým parametrem, jinak vrací ''​false''​.
 +      * Pro zjednodušení si lze představit jako operátor ''<''​.
 +    * Má jednu z těchto signatur (obecně -- záleží na typu, jaký mají prvky v množině):
 +      * <code cpp>​bool(const Type &, const Type &) const</​code>​
 +      * <code cpp>​bool(Type,​ Type) const</​code>​
 +      * <code cpp>​bool(const Type *, const Type *) const</​code>​
 +  * Je vhodné, aby měl bezparametrický konstruktor.
 +    * Když nenapíšeme žádný, tak se vygeneruje sám.
 +
 +Nyní je potřeba porovnat popisující vektory jednotlivých objektů. Pro tuto činnost se může hodit funkce ''​std::​lexicographical_compare''​ z hlavičkového souboru ''<​algorithm>''​.
 +
 +
 +
 +==== Krok 3 - Oprava operátorů ====
 +Opravte operátory tak, aby korektně pracovaly s kontejnerem ''​std::​set''​. Využijte metod ''​insert'',​ ''​find''​ a ''​erase''​ pro rychlý přístup k prvkům. Vytvořte si také vlastní ''​typedef''​ na ''​std::​set<​...>'',​ kterým nahradíte původní ''​typedef''​ na ''​std::​vector<​Geometry2D*>''​.
 +
 +
 +===== Vzorové řešení =====
 +
 +{{:​public:​pb161:​fall14_cviko10_reseni.zip|Řešení}}
  
QR Code
QR Code public:pb161:fall14_cviko10 (generated for current page)