Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

PB161 Dynamická alokace, polymorfismus 1 PB161 – Programování v jazyce C++ Objektově Orientované Programování Dynamická alokace, polymorfismus 6.10.2014.

Podobné prezentace


Prezentace na téma: "PB161 Dynamická alokace, polymorfismus 1 PB161 – Programování v jazyce C++ Objektově Orientované Programování Dynamická alokace, polymorfismus 6.10.2014."— Transkript prezentace:

1 PB161 Dynamická alokace, polymorfismus 1 PB161 – Programování v jazyce C++ Objektově Orientované Programování Dynamická alokace, polymorfismus

2 PB161 Organizační Pokud vám nechodí s vyhodnocením ●Každé odevzdávání má vloženu záměrnou prodlevu 15 minut, reálné vyhodnocení může trvat dalších 5-10 minut. ●Pokud vám nepřijde ani po 30 minutách, zkontrolujte prosím spambox, zda neskončil tam. ●Pokud ano, označte prosím jako špatně rozpoznaný spam ●Akci: Nahlásit jako chybně rozpoznaný spam  Proveď Vnitrosemestrálka ●V ISu již možné přihlašování Dynamická alokace, polymorfismus

3 PB161 CTU open CTU Open je česko-slovenské kolo mezinárodní soutěže ACM ICPC ●probíhá souběžně v 8 městech, mj. v Brně na FI. ●Web soutěže: Termín: ●pátek večer (tréninkové kolo) ●sobota (vlastní soutěž) Podoba soutěže: 3 lidi v týmu, 1 počítač, 5 hodin, cca 8 úloh Podporované jazyky: Java, C++, C. Dynamická alokace, polymorfismus

4 PB161 CTU open Úlohy zaměřené na návrh a implementaci algoritmů (styl "načti dlouhý vstup ze standardního vstupu, vypiš výsledek na standardní výstup") Ukázky úloh (loňský ročník): V případě zájmu o účast se registrujte na stránkách soutěže (http://contest.felk.cvut.cz/14prg/) a navíc pošlete mail se složením týmu Radkovi Pelánkovi který koordinuje soutěž v Dynamická alokace, polymorfismus

5 PB161 CTU open PB161 odměna: ●Za účast a vyřešení alespoň jednoho příkladu 3 bonus body ●Navíc za umístění v rámci lokálních týmů na FI 0-5 bodů (dle percentilu) ●Navíc za umístění v rámci všech týmů na FI 0-5 bodů (dle percentilu) (Pro každého člena týmu ) Dynamická alokace, polymorfismus

6 PB161 Dynamická alokace paměti 6 Dynamická alokace, polymorfismus

7 PB161 Dynamická alokace - motivace U statické alokace je velikost nebo počet známa v době překladu ● int array[100]; ● CClass a, b, c; Často ale tyto informace nejsou známy ●např. databáze se rozšiřuje ●např. textový popisek má typicky 20 znaků, ale může mít i 1000 ●neefektivní mít vždy alokováno 1000 znaků Dynamická alokace umožňuje vytvořit datovou položku (proměnnou, pole, objekt) až za běhu Dynamická alokace, polymorfismus

8 PB161 Statická vs. dynamická alokace Statická alokace alokuje na zásobníku (stack) ●automaticky se uvolňuje po dokončení bloku kódu ●typicky konec funkce, konec cyklu for… ●výrazně rychlejší (obsah zásobníku je typicky v cache) ●využití pro krátkodobé proměnné/objekty Dynamická alokace na haldě (heap) ●zůstává do explicitního uvolnění (nebo konce aplikace) ●využití pro dlouhodobé entity Dynamická alokace, polymorfismus

9 PB161 Dynamická alokace v C malloc() & free() Alokuje potřebný počet bajtů ●(musíme správně vypočíst) ●typicky používáme počet_prvků*sizeof(prvek) Přetypujeme na požadovaný typ (je riziko chyby) ● int* array = (int*) malloc(100 * sizeof(int)); Po konci práce uvolníme ●free(array); Dynamická alokace, polymorfismus

10 PB161 Dynamická alokace v C++ Operátor new & delete & delete[] Dva základní typy alokace ●alokace jedné entity (třída, jednoduchý typ, struktura) ●alokace pole objektů (několik entit v paměti za sebou) Zadáváme jen počet prvků daného typu ●potřebné místo v bajtech je automaticky dopočteno ●tj. typicky nepoužíváme sizeof(prvek) Vzniká entita s přiřazeným datovým typem ●typicky neprovádíme přetypování Je volán konstruktor objektu ●pokud alokuje pole objektů, musí být bezparametrický Dynamická alokace, polymorfismus A* pTest = new A; B* pTest2 = new B(20); A* array = new A[100];

11 PB161 Dynamická alokace jednoho objektu Je zavolán konstruktor (new) i destruktor (delete) Dynamická alokace, polymorfismus // Allocate A object dynamically // If fail, std::bad_alloc exception is thrown A* pTest2 = 0; pTest2 = new A(20); pTest2->print(); if (pTest2) { delete pTest2; pTest2 = 0; } class A { int m_value; public: A(int value) : m_value(value) {} void print() const {cout << m_value << endl; } };

12 PB161 Operátor -> vs. operátor. Operátor. se použije pro přístup k položkám objektu nebo struktury Pokud objekt alokujeme dynamicky ●máme proměnnou typu ukazatel ●operátor. nelze přímo použít ●musíme nejprve dereferencovat Pro zjednodušení je dostupný operátor -> ●(*pObjekt).atribut == pObjekt->atribut Dynamická alokace, polymorfismus A test1(20); test1.print(); A* pTest2 = new A(20); (*pTest2).print(); pTest2->print(); delete pTest2;

13 PB161 Chybové stavy při alokaci Pokud se alokace nepodaří, je vyvolána výjimka ●std::bad_alloc ●lze zachytit a reagovat (viz. přednáška výjimky později) Varianta operátoru new bez vyvolání výjimky ●int * p2 = new (nothrow) int; ●pokud se nezdaří, je vráceno NULL (=0) Dynamická alokace, polymorfismus

14 PB161 Dynamická alokace objektu bez výjimky Je zavolán konstruktor i destruktor Dynamická alokace, polymorfismus // Allocate A object dynamically, without exception A* pTest2 = new (std::nothrow) A(20); if (pTest2) { pTest2->print(); delete pTest2; pTest2 = 0; } class A { int m_value; public: A(int value) : m_value(value) {} void print() const {cout << m_value << endl; } };

15 PB161 Dynamické alokace bez volání konstruktoru Konstruktor není zavolán! Nepoužívá se často Dynamická alokace, polymorfismus class A { int m_value; public: A(int value) : m_value(value) {} void print() const {cout << m_value << endl; } }; // Allocate A object dynamically without constructor called A* pTest2 = static_cast (operator new (sizeof(A))); pTest2->print(); if (pTest2) { delete pTest2; pTest2 = 0; }

16 PB161 Dynamické alokace pole objektů Dynamická alokace, polymorfismus class B { int m_value; public: void setValue(int value) { m_value = value; } void print() const {cout << m_value << endl; } }; // Allocate array of integers const int NUM_ITEMS = 10; int* array = new int[10]; delete[] array; // Allocate array of A objects // A* pTest2 = new A[NUM_ITEMS]; // wrong: Why? B* pTest3 = new B[NUM_ITEMS]; for (int i = 0; i < NUM_ITEMS; i++) pTest3[i].setValue(5); if (pTest3) { delete[] pTest3; pTest3 = 0; } class A { int m_value; public: A(int value) : m_value(value) {} void print() const {cout << m_value << endl; } };

17 PB161 Dynamická alokace a dědičnost Třídu A a B budeme používat v dalších ukázkách Dynamická alokace, polymorfismus class A { protected: int m_value; public: void setValue(int value) { m_value = value; cout << "A::setValue() called" << endl; } virtual int getValue() const { cout << "A::getValue() called" << endl; return m_value; } static void printValue(int value) { cout << "Value = " << value << endl; cout << "A::printValue() called" << endl; } }; class B : public A { public: void setValue(int value) { m_value = value; cout << "B::setValue() called" << endl; } virtual int getValue() const { cout << "B::getValue() called" << endl; return m_value; } static void printValue(int value) { cout << "Value = " << value << endl; cout << "B::printValue() called" << endl; } };

18 PB161 Dynamická alokace a dědičnost Ukazatel na potomka můžeme přiřadit do ukazatele typu předka ● A* pObject = new B; ●na objekt typu B se přes rozhranní typu A Proběhne implicitní přetypování Dynamická alokace, polymorfismus // Retype B to A via pointers (compile time) A* pObject = new B; pObject->setValue(10); // from A pObject->getValue(); // from B (virtual) pObject->printValue(15);// from A (static)

19 PB161 Uvolňování paměti Jednotlivé objekty uvolníme pomocí delete ●nastavujte po uvolnění na = 0 pro zabránění opakovanému uvolnění Pole objektů uvolníme pomocí delete[] ●musíme použít ukazatel na začátek pole ●pokud použijeme ukazatelovou aritmetiku, tak uvolnění neproběhne Nepoužívaná, ale neuvolněná paměť => memory leaks ●problém hlavně u dlouhoběžících resp. paměťově náročných apps Dynamická alokace, polymorfismus

20 PB161 Memory leaks Dynamicky alokovaná paměť musí být uvolněna ●dealokace musí být explicitně provedena vývojářem ●(C++ nemá Garbage collector) Valgrind – nástroj pro detekci memory leaks (mimo jiné) ●valgrind -v --leak-check=full testovaný_program ●http://wiki.forum.nokia.com/index.php/Using_valgrind_with_Qt_Cre atorhttp://wiki.forum.nokia.com/index.php/Using_valgrind_with_Qt_Cre ator Microsoft Visual Studio ●automaticky zobrazuje detekované memory leaks v debug režimu Detekované memory leaks ihned odstraňujte ●stejně jako v případě warningu ●nevšimnete si jinak nově vzniklých ●obtížně dohledáte místo alokace Dynamická alokace, polymorfismus

21 PB161 Memory leaks - demo Dynamická alokace, polymorfismus #include int main(int argc, char* argv[]) { int* pArray = 0; int bVar; // Intensionally uninitialized pArray = new int[25]; if ((argc == 2) && atoi(argv[1]) == 1) { pArray = new int[25]; // Will cause "100 bytes in 1 blocks are definitely lost in loss record" Valgrind error } if (pArray) delete[] pArray; int* pArray2 = 0; pArray2 = new int[25]; pArray2 += 20; // Pointer inside pArray2 was changed, delete[] will not work correctly // Will cause "100 bytes in 1 blocks are definitely lost in loss record" Valgrind error if (pArray2) delete[] pArray2; // Will not delete allocated memory as pArray2 is different from address returned by new[] if (bVar) { // Will cause "Conditional jump or move depends on uninitialised value" Valgrind error pArray[10] = 1; } return 0; }

22 PB161 Chytré ukazatele (Smart pointers) Dynamická alokace, polymorfismus class MyClass { public: void MyFunction() {} }; class CSmart { void* m_ptr; public : CSmart(void* ptr) : m_ptr(ptr) {} ~CSmart() { delete m_ptr; } }; void foo() { MyClass* obj1 = new MyClass; CSmart smart(new MyClass); } int main() { foo(); return 0; } Bude memory leak? Které objekty? Problémy: 1.warning: deleting 'void*' is undefined 2.smart->MyFunction()?

23 PB161 Automatický ukazatel std::auto_ptr Velice omezený garbage collection ●#include ●založeno na šablonách (bude probráno později) auto_ptr p(new typ_třídy); Speciální objekt, který “obalí” naši třídu ●pokud zaniká, tak zavolá destruktor naší třídy a uvolní ji ●přetížené operátory * a -> ●s výsledkem pracujeme stejně jako s ukazatelem na třídu Využití pro neočekávané ukončení funkce ●např. vyvolána výjimka => mohl nastat memory leak Nelze využít pro pole (volá se delete, ne delete[]) Nepočítá zbývající reference (jako dělá Java,.NET) Deprecated (nedoporučeno pro použití) v C++11 Dynamická alokace, polymorfismus

24 PB161 Automatický ukazatel - ukázka Dynamická alokace, polymorfismus class A { int m_value; public: A(int value) : m_value(value) {} ~A() { cout << "~A() called" << endl; } void print() const {cout << m_value << endl; } }; #include void foo() { std::auto_ptr pValue(new A(20)); pValue->print(); (*pValue).print(); // No need to deallocate pValue } int main() { // foo() allocates dynamically, but uses auto_ptr for (int i = 0; i < 100; i++) { foo(); } return 0; } Deprecated (nedoporučeno pro použití) v C++11

25 PB161 Automatický ukazatel unique_ptr Další z chytrých ukazatelů, dostupný od C++11 ●https://en.wikipedia.org/wiki/Smart_pointer#unique_ptrhttps://en.wikipedia.org/wiki/Smart_pointer#unique_ptr auto_ptr nefunguje vhodně v případě, že kopírujeme ●původní auto_ptr zůstává prázdný, umístěný ukazatel se přesune do nového Dynamická alokace, polymorfismus std::unique_ptr p1(new int(5)); std::unique_ptr p2 = p1; //Compile error. //Transfers ownership. p3 now owns the memory and p1 is invalid. std::unique_ptr p3 = std::move(p1); p3.reset(); //Deletes the memory. p1.reset(); //Does nothing.

26 PB161 Ukazatele v C++11 V C a C++ bylo používáno NULL jako chybový ukazatel V C: #define NULL ((void*)0) ●typ ukazatel V C++: #define NULL 0 ●chybná interpretace – NULL má typ int ●problém při přetěžování V C++11: nullptr ●klíčové slovo ●je typu ukazatel – nikdy nebude typu int Dynamická alokace, polymorfismus

27 PB161 // Multi-dimensional array // Let's allocate 100x30 array of integers // 100 items array with elements int* int** array2D = new int*[100]; for (int i = 0; i < 100; i++) { // allocated 1D array on position array2D[i] array2D[i] = new int[30]; } array2D[10][20] = 1; // You need to delete it properly for (int i = 0; i < 100; i++) { delete[] array2D[i]; } delete[] array2D; Dynamická alokace vícedimenz. polí Céčkový přístup (lze využít i v C++) ●Nemusíme alokovat jen jednorozměrné (1D) pole ●2D pole je pole ukazatelů na 1D pole ●3D pole je pole ukazatelů na pole ukazatelů na 1D pole C++ nabízí navíc (lepší) ●pro 1D pole std::vector ●navíc automatické zvětšování ●možnost kontroly přístupu... ●(později) Dynamická alokace, polymorfismus

28 PB161 Dynamická alokace - poznámky Při dealokaci nastavte proměnnou zpět na NULL ●opakovaná dealokace nezpůsobí pád ●s výhodou využití std::unique_ptr nebo std::shared_ptr Při dynamické alokaci objektu je volán konstruktor ●u pole objektů je potřeba bezparametrický konstruktor Dynamická alokace je důležitý koncept ●je nutné znát a rozumět práci s ukazateli (viz. PB071) Dynamická alokace, polymorfismus

29 PB161 Polymorfismus 29 Dynamická alokace, polymorfismus

30 PB161 Motivace pro polymorfismus Může Microsoft/Torvalds vyvinout operační systém bez znalosti konkrétního hardware na kterém bude provozován? ●Tiskárny ●Harddisk ●CPU ●Grafická karta ●… Dynamická alokace, polymorfismus

31 PB161 Motivace pro polymorfismus - rozhraní Dynamická alokace, polymorfismus

32 PB161 Motivace pro polymorfismus Způsob jak v C++ realizovat rozhraní (interface) Možnost vývoje bez znalosti implementace ●Windows/Linux bez znalosti všech budoucích tiskáren Možnost změny implementace bez změny používajícího kódu Typy polymorfismu ●Přetížení – funkce se stejným jménem, ale různými argumenty ●Překrytí – funkce se stejným jménem i argumenty ●(Kombinace přetížení a překrytí) Dynamická alokace, polymorfismus

33 PB161 Polymorfismus - princip Zavolání metody se stejným jménem způsobí vykonání různého kódu ●např. add(float real), add(float real, float imag) ●např. metoda print() pro různé objekty K čemu je to dobré? ●můžeme mít speciální metody pro různé varianty argumentů ●nemusíme vědět, s jakým přesně objektem zrovna pracujeme ●jen víme, ze které třídy je potomek ●dokážeme pracovat s třídami, které nebyly známé v době psaní kódu ●jsou potomky tříd, které známy byly Dynamická alokace, polymorfismus

34 PB161 Polymorfismus – statická vazba 34 Dynamická alokace, polymorfismus

35 PB161 Typy polymorfismu - přetížení Více metod se stejným jménem, ale s různými typy anebo počty argumentů Během překladu poznáme, kterou metodu zavolat ●jde typicky o statickou vazbu ●ale může být i pozdní (viz. dále) Dynamická alokace, polymorfismus // Overloaded add method void add(float realPart) { m_realPart + = realPart; } void add(float realPart, float imaginary) { m_realPart += realPart; m_imagPart + = imaginary; } void add(const CComplexNumber &value) { setReal(m_realPart + value.getReal()); setImaginary(m_imagPart + value.getImaginary()); }

36 PB161 Včasná(statická) vazba (static binding) Včasnou vazbu může rozhodnout kompilátor při překladu Funkční volání je nahrazeno přímo adresou, kde je kód volané funkce Tuto adresu již nelze změnit později za běhu programu Rychlé! Dynamická alokace, polymorfismus

37 PB161 Typy polymorfismu – přetížení (2) Překladač musí být schopen rozhodnout volanou metodu Může být ovlivněno implicitním přetypování ●např. nemusíme mít metodu pro int, stačí pro float ●int se automaticky přetypuje na float ●souběžné metody pro int a float mohou způsobit problém Metody se v C++ nemohou lišit jen návratovým typem ●nemuselo by být jasné, kterou metody použít ●lze obejít pomocí funkčních objektů, overloadingByValueTrickDemo.cpp Dynamická alokace, polymorfismus class A { public: int MyFunction() { return 1; } const char* MyFunction() { return "1"; } }; int main() { A object; // Which one to call? object.MyFunction(); return 0; }

38 PB161 Přetížení - poznámky Není omezeno na metody objektů, lze i pro obyčejné funkce Zvolená funkce zůstává po kompilaci vždy stejná ●dokud není provedena rekompilace ●rychlejší a paměťově úspornější Dynamická alokace, polymorfismus int divFnc(int a, int b) { return a / b; } float divFnc(float a, float b) { return a / b; } int main() { // Přetížení funkcí int c = divFnc(10, 5); // int c = divFnc(10.4, 5.4); // problém, proč? return 0; }

39 PB161 Defaultní hodnoty parametrů metod Polymorfismus lze i přes defaultní parametry Částečná alternativa k několika přetíženým metodám ●defaultní hodnota argumentu se uvádí v deklaraci metody ● int div(int first, int second, int* pRem = NULL); ●pokud uživatel chce, dostane zpět i zbytek po dělení Za prvním argumentem s defaultní hodnotou už musí mít všechny defaultní hodnotu ●nelze int div(int a, int b = 0, int c); Dynamická alokace, polymorfismus

40 PB161 Defaultní hodnoty parametrů - použití Často se používá při pozdějším přidávání argumentů funkce ●není potřeba měnit stávající kód ●stávající kód jakoby použije defaultní hodnotu ●např. přidání možnosti získání zbytku po dělení ●int div(int first, int second); ●int div(int first, int second, int* pRem = NULL); Nově přidaný argument by ale neměl měnit původní chování funkce bez něj Dynamická alokace, polymorfismus

41 PB161 Psaní dobrého kódu Pokud přetěžujete metodu pouze pro různé varianty parametrů, ale většina kódu zůstává stejná, pak: ●vytvořte jednu metodu X implementující vlastní kód ●ostatní přetížené metody pouze předzpracují vstupní argumenty a zavolají X 41 int div(int first, int second) { return div(first, second, NULL); } int div(int first, int second, int* pRem) { //... } Dynamická alokace, polymorfismus

42 PB161 Přetížení – ukázka overloadingDemo.cpp CComplexNumber Přetížení konstruktorů Přetížení metody add Problém při podobnosti datových typů Problém při přetížení jen návratovou hodnotou Dynamická alokace, polymorfismus

43 PB161 Polymorfismus – pozdní vazba 43 Dynamická alokace, polymorfismus

44 PB161 Typy polymorfismu - překrytí Voláme metodu se stejným jménem i argumenty Metoda přesto může mít jinou implementaci (tělo) ●jinou pro objekt A a jinak pro objekt B ●pokud píšeme rychlejší XML parser, nemusíme vymýšlet nové jména metod od existujícího ●jinak pro předka a jinak pro jeho potomka ●potomek mění implementaci metody zděděné od předka ●rozhodnutí na základě reálného typu aktuálního objektu Jde o pozdní vazbu Dynamická alokace, polymorfismus

45 PB161 Pozdní vazba (dynamic binding) Během překladu není ještě pevně zafixován volaný kód Přímé funkční volání je nahrazeno náhledem do tabulky virtuálních metod konkrétního objektu ●teprve v tabulce je adresa cílové funkce, která se zavolá ●tabulku lze modifikovat za běhu => implementace zvolena za běhu Dynamická alokace, polymorfismus připomenutí – statická vazba

46 PB161 Pozdní vazba - syntaxe Klíčové slovo virtual Metodu označíme jako virtual v předkovi V potomcích už nemusíme ●pro lepší čitelnost lze uvádět Nelze změnit v potomcích zpět na statickou vazbu! Dynamická alokace, polymorfismus class A { public: virtual void foo() { cout << "A::foo()" << endl; } }; class B : public A { public: void foo() { cout << "B::foo()" << endl; } };

47 PB161 Vhodnost uvádění klíčového slova virtual Uvedené zápisy jsou funkčně ekvivalentní Vhodnější je druhý zápis – z deklarace derived je zřejmé, že foo() je virtuální Dynamická alokace, polymorfismus class base { public: virtual void foo(); }; class derived : public base { public: void foo(); } class base { public: virtual void foo(); }; class derived : public base { public: virtual void foo(); }

48 PB161 Překrytí – ukázka overridingDemo.cpp CComplexNumber, CComplexNumberCommented Přidání klíčového slova virtual Překrytí metody add(float real) a print() Zastínění metody add(float real, float imaginary) Dynamická alokace, polymorfismus

49 PB161 Polymorfismus – vhodnost použití Kdy použít statickou vazbu? Pokud je zřejmé, že potomci nebudou chtít překrýt dynamicky ●potomci mohou stále předefinovat nebo skrýt Pokud je žádoucí vysoká rychlost ●méně instrukcí pro zavolání ●lze použít dohromady s inline metodami ●namísto funkčního volání se vloží celý kód metody Dynamická alokace, polymorfismus

50 PB161 Polymorfismus – vhodnost použití (2) Kdy použít pozdní vazbu? Pro metody, které budou reimplementovat potomci po svém ●např. “vykresli” u grafického objektu ●zlepšuje rozšiřitelnost ●generická část kódu může být v předkovi Pokud typ objektu ovlivňuje chování třídy ●použití virtuálních funkcí ●jinak vhodnější použití šablony (template, viz. později) Dynamická alokace, polymorfismus

51 PB161 Polymorfismus – defaultní nastavení V C++ jsou všechny metody defaultně nevirtuální ●statická (včasná) vazba ●výhodné z důvodu rychlosti (statická je rychlejší) Některé OOP jazyky mají právě naopak ●v Javě jsou všechny metody defaultně virtuální Důvodem je kompromis mezi rychlostí a flexibilitou ●C++ straní rychlosti Dynamická alokace, polymorfismus

52 PB161 Hierarchie Dynamická alokace, polymorfismus Čistě abstraktní třída (bez implementace) Abstraktní třída (může být část implementace) Třída s implementací (děláme objekty)

53 PB161 Tabulka virtuálních funkcí Dynamická alokace, polymorfismus class A { virtual fooVirt(); virtual fooVirt2(); fooStatic(); } 0x1234 VTable 2 1 0x3344 0x class A { fooVirt(); fooVirt2(); fooStatic(); } 0x1234 0x6677 0x3344 všechny metody jsou volané brzkou vazbou přidáním virtual se adresa virtuálních metod změní na náhled od tabulky virtuálních metod VTable reálná adresa kódu metody pro danou metodu třídy

54 PB161 Datový typ je „maska“ Dynamická alokace, polymorfismus class A { virtual fooVirt(); virtual fooVirt2(); fooStatic(); } 0x1234 VTable 2 1 0x3344 0x pro virtuální metody jsou „vyřízlé“ díry na adresu metody ve VTable class A { virtual fooVirt(); virtual fooVirt2(); fooStatic(); } 0x1234 VTable 2 1 0x3344 0x Pro metody volané včasnou vazbou je přímo adresa kódu přímo na masce maska typu A

55 PB161 Přetypování B na A Dynamická alokace, polymorfismus class B : public A { virtual fooVirt(); virtual fooVirt2(); fooStatic(); fooStatic2(); } 0x8844 class B : public A { virtual fooVirt(); virtual fooVirt2(); fooStatic(); fooStatic2(); } 0x1234 VTable 2 1 0x5836 0x Pro metody volané včasnou vazbou je přímo adresa kódu na masce. Pokud se podíváme přes masku A na třídu B, tak bude fooStatic() vždy 0x1234 (adresa z A), nikoli 0x8844 (adresa z B) VTable 2 1 0x5836 0x pohledem přes masku A získáme adresy metod z VTable třídy B A* prom = new B(); IPrinter* printer = new CHPDeskJet5550(); maska typu A 0x7403 fooStatic2() není přes masku A dostupná

56 PB161 Dotaz z minulé hodiny Dynamická alokace, polymorfismus #include using namespace std; class X { public: virtual void foo() { cout << "X" << endl; } virtual ~X() {} }; class Y : public X { public: virtual void foo() { cout << "Y" << endl; } }; int main() { X* pers1 = new X; Y* pers2 = new Y; X* pers3 = new Y; pers1->foo(); pers2->foo(); pers3->foo(); delete pers1; delete pers2; delete pers3; return 0; } #include using namespace std; class X { public: virtual void foo() { cout << "X" << endl; } virtual ~X() {} }; class Y : private X { public: virtual void foo() { cout << "Y" << endl; } }; int main() { X* pers1 = new X; Y* pers2 = new Y; // error: 'X' is an inaccessible base of 'Y' X* pers3 = new Y; pers1->foo(); pers2->foo(); pers3->foo(); delete pers1; delete pers2; delete pers3; return 0; } : private X

57 PB161 Konstruktory & destruktory Dynamická alokace, polymorfismus

58 PB161 Konstruktor - připomenutí Metoda automaticky volaná při vytváření objektu Možnost více konstruktorů (přetížení) Možnost konstruktoru s parametry Dynamická alokace, polymorfismus

59 PB161 Konstruktor – pořadí volání v dědičnosti V jakém pořadí budou konstruktory volány? ●konstruktory předka se volají před konstruktory potomka Destruktory se volají v opačném pořadí Dynamická alokace, polymorfismus class X { public: X() { cout << "Constructor X" << endl; } }; class Y : public X { public: Y() { cout << "Constructor Y" << endl; } }; class Z : public Y { public: Z() { cout << "Constructor Z" << endl; } }; int main() { Z object1; return 0; }

60 PB161 Kopírovací (copy) konstruktory Konstruktor pro vytváření kopie existujícího objektu ●volány ve více situacích (přiřazení, funkční argument...) Pokud nevytvoříte vlastní, překladač vygeneruje automaticky vlastní! Dva základní typy konstruktorů: plytký a hluboký Dynamická alokace, polymorfismus

61 PB161 Pravidlo tří (Rule of three) Každá třída X má defaultní implementaci ●Konstruktoru (bez parametrů, tj. X()) ●Kopírovacího konstruktoru, operátoru = () ●Destruktoru (nevirtuální, ~X()) Pokud poskytnete vlastní implementaci pro některý z nich, tak poskytněte validní implementaci i pro ostatní https://stackoverflow.com/questions/ /wha t-is-the-rule-of-three https://stackoverflow.com/questions/ /wha t-is-the-rule-of-three Dynamická alokace, polymorfismus

62 PB161 Kdy jsou volány kopírovací konstruktory? Při použití funkcí s argumenty typu třída ●pokud je argument předáván hodnotou ●void foo(CClass X) {}; Když má funkce návratovou hodnotu typu třída ●CClass foo() { CClass X; return X; } Když je objekt použit pro inicializaci jiného objektu ●třída je parametr konstruktoru druhého objektu ●CClass x; CClass y(x); Není volán (pro daný argument) při jeho předávání referencí/odkazem Dynamická alokace, polymorfismus

63 PB161 Syntaxe kopírovacích konstruktorů jméno_třídy(const jméno_třídy & předloha) Využívá se const ●neměníme původní objekt Využívá se předání referencí & ●nechceme volat opakovaně znovu kopírovací konstruktor Dynamická alokace, polymorfismus Person(const Person& copy) : m_name(0) { cout << "Copy constructor Person called" << endl; setName(copy.getName()); }

64 PB161 Plytký (shalow) copy konstruktor Kopíruje pouze hodnoty atributů ●defaultní varianta – vytvářena překladačem Není problém pro jednoduché datové typy (int) Problém při atributu typu ukazatel nebo složitější objekty ●např. při atributu typu char* zkopíruje se pouze hodnota ukazatele, ale nevytvoří se celé druhé pole Dynamická alokace, polymorfismus

65 PB161 Hluboký (deep) copy konstruktor Je nutné explicitně implementovat Proveden dodatečný uživatelský kód ●typicky vytvoření a kopírování dynamických polí atp. Zajistí vytvoření kompletní kopie původního objektu Pokud deklarujete, tak také překryjte operátor přiřazení = Dynamická alokace, polymorfismus

66 PB161 Kopírovací konstruktor - ukázka Dynamická alokace, polymorfismus class Person { char* m_name; public: Person(const char* name) : m_name(0) { cout << "Constructor Person(" << name << ") called“; cout << endl; setName(name); } Person(const Person& copy) : m_name(0) { cout << "Copy constructor Person called" << endl; setName(copy.getName()); } virtual ~Person() { if (m_name) delete[] m_name; } const char* getName() const { return m_name; } void setName(const char* name) { if (m_name) delete[] m_name; m_name = new char[strlen(name) + 1]; strncpy(m_name, name, strlen(name) + 1); } }; int main() { // // Demo for copy constructor // Person object1("Prvni"); cout << object1.getName() << endl; Person object2(object1); cout << object2.getName() << endl; // // Demo for assignment // // No copy constructor called, just primitive values assignment // (pointer to char array) object2 = object1; // Seems good at the moment, but... cout << object2.getName() << endl; // Changing name in object2 will also change name in object1 object2.setName("Problem"); cout << object1.getName() << endl; return 0; }

67 PB161 Operátor přiřazení = Při použití pro primitivní hodnoty zkopíruje samotnou hodnotu Při použití pro třídní datový typ provede kopii hodnot všech atributů Defaultní operátor přiřazení se chová stejně jako defaultní kopírovací konstruktor ●operátor přiřazení lze také předefinovat (později) ●Vhodná implementace pomocí copy&swap idiomu (později) Dynamická alokace, polymorfismus

68 PB161 Co je konstruktor s explicit ? Každá třída má alespoň implicitní konstruktor ●bez parametrů (bezparametrický konstruktor) ●pokud deklarujeme další konstruktor (typicky s argumenty), tak se implicitní konstruktor nevytváří Objekt ale můžeme vytvářet i pomocí argumentů s typem, který neodpovídá typu v konstruktoru. Proč? ●pokud je to možné, dojde k implicitní typové konverzi ●použije se kopírovací konstruktor Klíčovým slovem explicit říkáme, že daný konstruktor může být použit jen v případě, že přesně odpovídá typ argumentů ●ochrana proti nečekané implicitní typové konverzi Dynamická alokace, polymorfismus

69 PB161 explicit - ukázka Dynamická alokace, polymorfismus class MyClass { public: MyClass(float X) {} }; class MyClass2 { public: explicit MyClass2(float X) {} }; int main() { MyClass object = 10; // OK MyClass2 object2 = 10; //error //error: conversion from 'int' to non-scalar type 'MyClass2' requested return 0; }

70 PB161 Destruktory Určeno pro úklid objektu ●uvolnění polí a dynamických struktur ●uzavření spojení apod. Automaticky voláno při uvolňování objektu ●statická i dynamická alokace Může být pouze jediný (nelze přetěžovat) Nemá žádné parametry Dynamická alokace, polymorfismus

71 PB161 Destruktor - syntaxe Syntaxe ~jméno_třídy() Stejné jméno jako třída Nevrací návratovou hodnotu U C++ vždy voláno při zániku objektu ●konec platnosti lokální proměnné ●dealokace dynamicky alokovaného objektu ●odlišnost od Javy (Garbage collection) Dynamická alokace, polymorfismus

72 PB161 Proč virtuální destruktor? 72 Dynamická alokace, polymorfismus

73 PB161 Problém s nevirtuálním destruktorem Dynamická alokace, polymorfismus class IPrinter { public: virtual string GetPendingDocuments() const = 0; virtual void PrintDocument(const string& document) = 0; }; class CHPDeskJet5550 : public IPrinter { string m_printedDocument; public: CHPDeskJet5550() {} virtual string GetPendingDocuments() const { return m_printedDocument; } virtual void PrintDocument(const string& document) { m_printedDocument = document; } ~CHPDeskJet5550() { cout << "~CHPDeskJet5550() called"; } }; void demoNonVirtDestructor() { // Create new HPDeskJet5550 printer IPrinter* printer = new CHPDeskJet5550(); // Use printer without knowledge about its type // We are using IPrinter interface printer->PrintDocument("secret"); cout GetPendingDocuments() << endl; // ??? which destructor will be called? delete printer; // Why not ~CHPDeskJet5550()? }

74 PB161 Virtuální destruktor Je velmi vhodné deklarovat už na úrovni rozhraní ●virtual ~IPrinter() {}; ●nevíme předem, zda nebude některý potomek potřebovat ●pokud neuděláme, nutíme potomka provádět úklid jiným způsobem ●speciální „úklidová“ metoda (omezuje výhody dědičnosti) ●využití automatických ukazatelů (nelze pro vše) ●poskytujeme i prázdnou implementaci Typické použití u tříd vyžadující explicitní úklid ●pokud proběhla dynamická alokace (dealokace) ●pokud je potřeba uvolnit systémové prostředky (socket, kontext...) Dynamická alokace, polymorfismus

75 PB161 Hierarchie doplněná o virt. destruktor Dynamická alokace, polymorfismus Čistě abstraktní třída (bez implementace) Abstraktní třída (může být část implementace) Třída s implementací (děláme objekty)

76 PB161 Virtuální destruktor - warning g++ warning: deleting object of polymorphic class type 'Printer' which has non-virtual destructor might cause undefined behaviour [- Wdelete-non-virtual-dtor] Dynamická alokace, polymorfismus class Printer { public: virtual void print() {} }; int main() { Printer* var1 = new Printer; delete var1; return 0; }

77 PB161 Shrnutí Dynamická alokace ●vznik objektů na haldě ●nutnost dealokace ●volání destruktoru Polymorfismus – velmi důležitý koncept ●přetížení (overloading) ●překrytí (overridding) Konstruktory, destruktory… pravidlo tří Dynamická alokace, polymorfismus


Stáhnout ppt "PB161 Dynamická alokace, polymorfismus 1 PB161 – Programování v jazyce C++ Objektově Orientované Programování Dynamická alokace, polymorfismus 6.10.2014."

Podobné prezentace


Reklamy Google