Principy OOP - rozhraní, dědičnost PB161



Podobné dokumenty
PRINCIPY OOP, DĚDIČNOST. PB161 Principy OOP - Zapouzdření

Programování v C++ 1, 6. cvičení

PB161 Programování v jazyce C++ Přednáška 3

Dědění, polymorfismus

Generické programování

Programování v C++ 1, 5. cvičení

Programování v C++ 3, 3. cvičení

Objektové programování

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

PB161 Programování v jazyce C++ Přednáška 7

PB161 Programování v jazyce C++ Přednáška 7

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue.

Programování v C++ 2, 4. cvičení

PB přednáška (5. října 2015)

Více o konstruktorech a destruktorech

PB161 Programování v jazyce C++ Přednáška 9

1. Dědičnost a polymorfismus

PB161 Programování v jazyce C++ Přednáška 4

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

Chování konstruktorů a destruktorů při dědění

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

24. listopadu 2013, Brno Připravil: David Procházka

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

Jazyk C# (seminář 3)

PB161 Programování v jazyce C++ Přednáška 1

Abstraktní třídy, polymorfní struktury

20. Projekt Domácí mediotéka

PB161 Základy OOP. Tomáš Brukner

PB161 Programování v jazyce C++ Přednáška 10

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Základy objektové orientace I. Únor 2010

PREPROCESOR POKRAČOVÁNÍ

Funkční objekty v C++.

Dědičnost. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 23.března

PB161 Programování v jazyce C++ Přednáška 8

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

Virtuální metody - polymorfizmus

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Jazyk C++ I. Polymorfismus

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

PB161 Programování v jazyce C++ Přednáška 4

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Programování II. Polymorfismus

Zpracoval:

Objekty v PHP 5.x. This is an object-oriented system. If we change anything, the users object.

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

8 Třídy, objekty, metody, předávání argumentů metod

Programování II. Návrh programu II

Konstruktory a destruktory

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám.

Dědičnost (inheritance)

<surface name="pozadi" file="obrazky/pozadi/pozadi.png"/> ****************************************************************************

Programování v C++ 2, 7. cvičení

přetížení operátorů (o)

Výčtový typ strana 67

IRAE 07/08 Přednáška č. 1

Zapouzdření. Tomáš Pitner, upravil Marek Šabo

PB přednáška (21. září 2015)

Statické proměnné a metody. Tomáš Pitner, upravil Marek Šabo

PB161 Programování v jazyce C++ Přednáška 10

přetížení operátorů (o)

Dynamická identifikace typů v C++.

NPRG031 Programování II 1 / :25:46

NMIN201 Objektově orientované programování 1 / :36:09

Jazyk C# (seminář 6)

typová konverze typová inference

PB161 Programování v jazyce C++ Přednáška 9

Vyřešené teoretické otázky do OOP ( )

Viditelnost (práva přístupu) Tomáš Pitner, upravil Marek Šabo

Mělká a hluboká kopie

Jazyk C++ 1. Blok 3 Objektové typy jazyka C++ Třída. Studijní cíl. Doba nutná k nastudování. Průvodce studiem

11 Diagram tříd, asociace, dědičnost, abstraktní třídy

Pokročilé programování v jazyce C pro chemiky (C3220) Dědičnost tříd v C++

Úvod do programovacích jazyků (Java)

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

PB161 Programování v jazyce C++ Přednáška 9

Programování v C++ 2, 8. cvičení

Pokročilé programování v jazyce C pro chemiky (C3220) Třídy v C++

Návrhové vzory OMO, LS 2014/2015

Programování v C++ VI

Programování v jazyce C a C++

konstruktory a destruktory (o)

PB161 Programování v jazyce C++ Přednáška 8

Objektově orientované programování. Úvod

Jazyk C++ I. Šablony 2

PŘETĚŽOVÁNÍ OPERÁTORŮ

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/

Ukazka knihy z internetoveho knihkupectvi

Třídy, polymorfismus. A0B36PR2-Programování 2 Fakulta elektrotechnická České vysoké učení technické

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members)

PB161 Programování v jazyce C++ Objektově Orientované Programování

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

1. Programování proti rozhraní

PB161 Programování v jazyce C++ Přednáška 2

Programování II. Abstraktní třída Vícenásobná dědičnost 2018/19

Programování v jazyce C a C++

Pokročilé programování v jazyce C pro chemiky (C3220) Pokročilá témata jazyka C++

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Abstraktní třída a rozhraní

Transkript:

Principy OOP - rozhraní, dědičnost 1 Principy OOP - Dědičnost, rozhraní 29.9.2014

Co nás dnes čeká Motivace pro a realizace rozhraní Dědičnost a kompozice Operátor reference & 2 Principy OOP - Dědičnost, rozhraní 29.9.2014

Rozhraní 3 Principy OOP - Dědičnost, rozhraní 29.9.2014

Motivace pro rozhraní 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 4 Principy OOP - Dědičnost, rozhraní 29.9.2014

Motivace pro rozhraní 5 Principy OOP - Dědičnost, rozhraní 29.9.2014

Principy OOP - Dědičnost, rozhraní 29.9.2014

Existující kód int main() { IPrinter* printer = GetSelectedPrinter(); printer->printdocument(); printer->getpendingdocuments(); } return 0; Nový kód IPrinter* GetSelectedPrinter() { // user selects printer via GUI // e.g., InkPrinter -> selectedprinter // e.g., LaserPrinter -> selectedprinter return selectedprinter; } 7 Principy OOP - Dědičnost, rozhraní 29.9.2014

Rozhraní a implementace 1. Deklarace rozhraní (abstraktní třída, obecná tiskárna) Virtuální metody (virtual) Virtuální destruktor 2. Implementace konkrétní třídy (konkrétní tiskárna) Dědičnost Implementace metod rozhraní (obecné tiskárny) 3. Použití konkrétní instance (konkrétní tiskárna) Vytvoření instance konkrétní třídy, dynamická alokace Přetypování potomka na předka (rozhraní) Použití konkrétní tiskárny skrze rozhraní (obecná tiskárna) 4. Zrušení instance (konkrétní tiskárna) Hodí se nám virtuální destruktor 8 Principy OOP - Dědičnost, rozhraní 29.9.2014

1. Deklarace rozhraní Jméno rozhraní (velké I před jménem je pouze konvence) Klíčové slovo virtual umožní potomkům poskytnout vlastní implementaci metod rozhraní. class IPrinter { public: virtual string GetPendingDocuments() const = 0; virtual void PrintDocument(const string& document) = 0; virtual ~IPrinter() {} }; Rozhraní by mělo vždy poskytovat virtuální destruktor tak, aby potomci mohli implementovat vlastní úklid, pokud potřebují. Prázdná implementace {} je poskytnuta proto, aby potomek nebyl nucen destruktor implementovat, pokud to nepotřebuje. Principy OOP - Dědičnost, rozhraní 29.9.2014 = 0 označuje deklaraci funkce, u které nebude poskytnuta implementace. Implementaci poskytují potomci.

2. Implementace konkrétní třídy class CHPDeskJet5550 : public IPrinter { string m_printeddocument; public: CHPDeskJet5550() {} Konkrétní třída CHPDeskJet5550 dědí z rozhraní IPrinter (tzv. implementuje rozhraní). V případě metod rozhraní = 0 musí poskytnout implementaci, jinak nelze tvořit instance. }; virtual string GetPendingDocuments() const { return m_printeddocument; } virtual void PrintDocument(const string& document) { m_printeddocument = document; } ~CHPDeskJet5550() { cout << "~CHPDeskJet5550() called"; } Konkrétní implementace metod z rozhraní (předka) Třída může poskytnout vlastní destruktor s vlastním úklidem Principy OOP - Dědičnost, rozhraní 29.9.2014

3. Použití konkrétní instance // HPDeskJet5550 printer, allocation on stack CHPDeskJet5550 printer1; printer1.printdocument("secret"); // Create new HPDeskJet5550 printer, dynamic allocation on heap CHPDeskJet5550* printer2 = new CHPDeskJet5550(); printer2->printdocument("secret"); Vytvoření proměnné printer1 typu CHPDeskJet5550 na zásobníku a její použití Dynamická alokace proměnné printer2 na haldě // Create new HPDeskJet5550 printer, retype to base class (interface) IPrinter* printer3 = new CHPDeskJet5550(); Přetypování dynamicky printer3->printdocument("secret"); alokovaného objektu typu cout << printer3->getpendingdocuments() << endl; CHPDeskJet5550 na typ předka - rozhraní // printer1 is automatically removed when function ends // dealloaction of instance CHPDeskJet5550 // destructor CHPDeskJet5550::~CHPDeskJet5550 is called delete printer2; // destructor CHPDeskJet5550::~CHPDeskJet5550 is called, // because IPrinter::~IPrinter is virtual destructor delete printer3; Použití objektu konkrétní tiskárny jen prostřednictvím rozhraní Principy OOP - Dědičnost, rozhraní 29.9.2014

4. Zrušení instance // HPDeskJet5550 printer, allocation on stack CHPDeskJet5550 printer1; printer1.printdocument("secret"); // Create new HPDeskJet5550 printer, dynamic allocation on heap CHPDeskJet5550* printer2 = new CHPDeskJet5550(); printer2->printdocument("secret"); // Create new HPDeskJet5550 printer, retype to base class (interface) IPrinter* printer3 = new CHPDeskJet5550(); Objekt printer1 na printer3->printdocument("secret"); zásobníku se zruší cout << printer->getpendingdocuments() << endl; automaticky při konci funkce // printer1 is automatically removed when function ends // dealloaction of instance CHPDeskJet5550 // destructor CHPDeskJet5550::~CHPDeskJet5550 is called delete printer2; // destructor CHPDeskJet5550::~CHPDeskJet5550 is called, // because IPrinter::~IPrinter is virtual destructor delete printer3; Principy OOP - Dědičnost, rozhraní 29.9.2014 Dealokace dynamicky alokovaných objektů z haldy Objekt printer3 je typu IPrinter, díky virtuálnímu destruktoru se zavolá i destruktor třídy CHPDeskJet5550

Rozhraní a implementace (rekap.) 1. Deklarace rozhraní (abstraktní třída, obecná tiskárna) Virtuální, čistě abstraktní metody 2. Implementace konkrétní třídy (konkrétní tiskárna) Dědičnost, Polymorfismus, Zapouzdření Implementace metod rozhraní (obecné tiskárny) 3. Použití konkrétní instance (konkrétní tiskárna) Přetypování potomka na předka (rozhraní) Použití konkrétní tiskárny prostřednictvím rozhraní Zapouzdření 4. Zrušení instance (konkrétní tiskárna) delete, virtuální destruktor 13 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost 14 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost v OOP Dědičnost je nástroj pro podporu abstrakce potomci dodržují rozhraní zavedené předkem mohou ale měnit chování (implementaci) můžeme mít generický kód, který bude pracovat s budoucími implementacemi Dědičnost je nástroj pro omezení duplicity v kódu Duplicita v kódu je nepříjemná snižuje přehlednost zvyšuje náročnost úprav (nutno na více místech) zvyšuje riziko chyby (někde zapomeneme upravit) dědit z třídy jen pro využití části funkčnosti ale není dobré (viz. dále) Zlepšuje možnost znovuvyužití existujícího kódu 15 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost v C V C se abstrakce a znovuvyužití kódu dosahuje: dobrým návrhem funkčního rozhraní umístěním do samostatných hlavičkových souborů přechod na jinou implementaci ideálně jen změnou hlavičkového souboru V C se duplicita kódu odstraňuje: vytvořením nové funkce a vložením funkčního volání na původní místa 16 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost v C++ V C++ existuje systematičtější podpora lze odstranit duplicitu i pro proměnné navíc podporuje silnou typovou kontrolu Mechanismus umožňující vytvořit další třídu (potomek) s využitím předlohové třídy (předek) potomek zdědí možnosti předka (atributy a metody) může je rozšiřovat a předefinovat (překrývat) 17 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost postup při abstrakci Máme dvě (nebo víc) entit se společným chováním Snažíme se vytvořit společnou logiku (metody), které budou potřebné chování pro všechny entity popisovat aniž bychom museli vědět, se kterou právě pracujeme tvoříme rozhraní Vytvoříme novou třídu (předka, rozhraní) obsahující popis společného rozhraní (veřejné metody) Pro jednotlivé entity vytvoříme nové samostatné třídy, které budou implementovat definované rozhraní Nové třídy budou potomci třídy definující rozhraní 18 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost - syntaxe #include "cmousebase.h" Potomek Hlavičkový soubor předka class CFieldMouse : public CMouseBase { public: Předek CFieldMouse(); protected: bool increasesizebyfood(const unsigned int foodamount); }; Modifikátor definující způsob dědění metod a atributů 19 Principy OOP - Dědičnost, rozhraní 29.9.2014

Přístupová práva - protected K položce s právem protected má přístup pouze potomek atribut může být čten a měněn potomkem metoda nemůže být volána zvenčí Jako protected typicky označujeme metody které nemají být dostupné všem, ale potomkům ano často jde o virtuální přetěžované metody (později) méně často atributy raději protected setter metodu 20 Principy OOP - Dědičnost, rozhraní 29.9.2014

Typová hierarchie Dědičnost vytváří hierarchii objektů od nejobecnějšího k nejspecifičtějším Na místo předka může být umístěn potomek proměnná s typem předka může obsahovat potomka potomek může být argumentem funkce s typem předka zároveň zachována typová bezpečnost Při dědění lze omezit viditelnost položek předka specifikace práv při dědění 21 Principy OOP - Dědičnost, rozhraní 29.9.2014

Specifikátory přístupových práv dědění public (class B : public A {}; ) zděděné položky dědí přístupová práva od předka práva zůstanou jako předtím private (class B : private A {}; ) zděděné položky budou private, odvozená třída však bude mít přístup ke položkám, pokud byly v předkovi public nebo protected nebude přístup k položkám private u předka v potomcích potomka už nebude přístup používáme, pokud nechceme být předkem 22 Principy OOP - Dědičnost, rozhraní 29.9.2014

Specifikátory dědění přístupových práv (2) protected (class B : protected A {}; ) položky private a protected zůstanou stejné, z public se stane protected pokud neuvedeme (class B : A {}; ) class jako private, u struct a union jako public virtual (class B : virtual A {}; ) lze kombinovat s jedním z předchozích, přikazuje pozdní vazbu (viz později) 23 Principy OOP - Dědičnost, rozhraní 29.9.2014

Práva při dědění - ukázka inheritancerightsdemo.cpp přístup k private metodě přístup při dědění public/private/protected změna práv pro přístup při opakovaném dědění způsob znepřístupnění původně public metody 24 Principy OOP - Dědičnost, rozhraní 29.9.2014

kód z inheritancerightsdemo.cpp // // Inheritance rights demo // class A { private: void privatemethoda() {} protected: void protectedmethoda() {} public: void publicmethoda() {} }; /** Public inheritance - all rights for inherited methods/atributes stay same */ class B : public A { public: void test() { //privatemethoda(); // error: 'void A::privateMethodA()' is private protectedmethoda(); // OK publicmethoda(); // OK } }; /** Private inheritance - all rights for inherited methods/atributes changes to private */ class C : private A { public: void test() { //privatemethoda(); // error: 'void A::privateMethodA()' is private protectedmethoda(); // OK, but protectedmethoda is now private in C publicmethoda(); // OK, but publicmethoda is now private in C } }; Principy OOP - Dědičnost, rozhraní 29.9.2014 25

kód z inheritancerightsdemo.cpp (2) /** Public inheritance from C (C was inherited privately from A) } */ }; class D : public C { public: void test() { //privatemethoda(); // error: 'void A::privateMethodA()' is private //protectedmethoda(); // error: 'void A::protectedMethodA()' is protected //publicmethoda(); // error: 'void A::publicMethodA()' is inaccessible } }; class C : private A { public: void test() { //privatemethoda(); protectedmethoda(); publicmethoda(); /** Protected inheritance - all rights for inherited methods/atributes changes to private */ class E : protected A { public: void test() { //privatemethoda(); // error: 'void A::privateMethodA()' is private protectedmethoda(); // OK, protectedmethoda stays protected in E publicmethoda(); // OK, but publicmethoda is now protected in E } }; /** Public inheritance from E (E was inherited as protected from A) */ class F : public E { public: void test() { //privatemethoda(); // error: 'void A::privateMethodA()' is private }; } protectedmethoda(); // OK publicmethoda(); // OK 26 Principy OOP - Dědičnost, rozhraní 29.9.2014

Jak dědit z více existujících tříd? Nová třída má mít vlastnosti více entit Novou třídu lze přetypovat na více různých předků v Jave se řeší pomocí interfaces V C++ lze řešit pomocí násobné dědičnosti (třída má více předků) pomocí kompozice objektu (třída má více podčástí) 27 Principy OOP - Dědičnost, rozhraní 29.9.2014

Syntaxe násobné dědičnosti class CRAMMemory { int m_ramsize; Předek 1 public: CRAMMemory(unsigned int size) { m_ramsize = size; } int getramsize() const { return m_ramsize; } }; Předek 2 class CCPU { int m_clockfrequency; public: CCPU(unsigned int freq) { m_clockfrequency = freq; } int getcpufreq() const { return m_clockfrequency; } }; Dědíme z obou předků class CNotebookInherit : public CRAMMemory, public CCPU { public: CNotebookInherit(unsigned int ramsize, unsigned int cpufreq) : CRAMMemory(ramSize), CCPU(cpuFreq) { this-> CCPU::getCPUFreq(); } int getcpufreq() const { return m_clockfrequency; } }; Syntakticky správně, je ale vhodné? Principy OOP - Dědičnost, rozhraní 29.9.2014 28

Dědičnost vs. kompozice Dědičnost je Is-A vztah (chová se je jako A) potomek má všechny vnější vlastnosti předka A potomka můžeme přetypovat na předka (je správné se na notebook dívat jako na případ CPU?) Kompozice je Has-A vztah třída může mít jako atribut další třídu A hodnotou, referencí, ukazatelem třída obsahuje vlastnosti A a další třída může mít víc tříd jako své atributy (je vhodnější se na notebook dívat jako na složeninu CPU a RAM?) 29 Principy OOP - Dědičnost, rozhraní 29.9.2014

Kompozice namísto násobné dědičnosti class CNotebookInherit : public CRAMMemory, public CCPU { }; Násobná dědičnost Kompozice class CNotebookComposition { CRAMMemory m_ram; CCPU m_cpu; public: /** Initialize atributes in constructor. As params are passed into constructors of attributes, inicialization list section needs to be used */ CNotebookComposition(unsigned int ramsize, unsigned int cpufreq) : m_ram(ramsize), m_cpu(cpufreq) {} int getcpufreq() const { return m_cpu.getcpufreq(); } int getramsize() const { return m_ram.getramsize(); } }; 30 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost vs. kompozice - ukázka inheritancecompositiondemo.cpp násobná dědičnost kompozice využití inicializační sekce konstruktoru přetypování na předka 31 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost vhodnost použití Dle použití lze volit mezi dědičností a kompozicí Obecně preference kompozice před dědičností násobná dědičnost může být nepřirozená ale kompozice může být kódově rozsáhlejší Možná i kombinace objekt obsahuje kompozicí třídy jako atributy jednotlivé atributy mohou mít hierarchii dědičnosti 32 Principy OOP - Dědičnost, rozhraní 29.9.2014

Existující kód int main() { IPrinter* printer = GetSelectedPrinter(); printer->printdocument(); printer->getpendingdocuments(); } return 0; Nový kód IPrinter* GetSelectedPrinter() { // user selects printer via GUI // e.g., InkPrinter -> selectedprinter // e.g., LaserPrinter -> selectedprinter return selectedprinter; } 33 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost správné použití (1) Z nového kódu můžeme vždy volat kód existující aniž bychom přepisovali existující kód (víme jméno a parametry existující funkce, nový kód přizpůsobíme) Dědičnost nám umožňuje volat z existujícího kódu kód, který teprve bude napsán existující kód pracuje s předkem (např. IPrinter) (IPrinter deklarován v době psaní existujícího kódu) nový kód vytváří potomky (např. CInkPrinter) existující kód pracuje s IPrinter CInkPrinter lze přetypovat na IPrinter existující kód může používat CInkPrinter (jako IPrinter) Potomek by neměl měnit logiku chování (rozhraní) předka! CInkPrinter pořád přijímá dokument na tisk pouze tiskne specializovaným způsobem 34 Principy OOP - Dědičnost, rozhraní 29.9.2014

Multifunkční zařízení (tiskárna + scanner) Řešení pomocí dědičnosti Řešení pomocí kompozice 35 Principy OOP - Dědičnost, rozhraní 29.9.2014

Multifunkční zařízení (2) Je lepší použít dědičnost nebo kompozici? Pokud se jednotlivý předkové funkčně nepřekrývají, tak lze vícenásobná dědičnost vhodné je dědit z čistě abstraktních tříd (viz. dále) pak je stejné jako rozhraní v Javě (interfaces) IPrinter a IScanner pokrývají odlišnou funkčnost Dědičnost používáme, pokud předpokládáme přetypování potomka na předka u CInkPrinter bude nastávat bude nastávat i u CScanPrintDev? Dědičnost používáme, pokud předpokládáme později vznik potomků z naší třídy 36 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost správné použití (2) Dědičnost je velice silný vztah => jeho nevhodné použití může přinést problémy Potomci při dědičnosti mají specializovat, ne rozšiřovat funkčnost základního objektu CStudentTeacher není jen speciální případ studenta CStudentTeacher je student a učitel zároveň => víc než jen student => dědičnost není vhodná vhodnější je zde kompozice (CStudentTeacher obsahuje atributy CStudent a CTeacher) Platí že potomek je substituovatelný za předka? pokud ano, použijte dědičnost Preferujte kompozici před dědičností 37 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost postup při duplicitě Máme dvě (nebo víc) tříd se společným chováním Identifikujeme společnou logiku (metody) Identifikujeme společná data (atributy) Vytvoříme novou třídu (předka) obsahující společné atributy a logiku Odstraníme přesunuté atributy&metody z původních tříd Původní třídy nastavíme jako potomky nového předka 38 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost - příklad V laboratoři máme domácí a polní myš. Rozdíl mezi druhy je jen v počáteční velikosti a rychlosti přibírání po požití potravy. Nové třídy CHouseMouse a CFieldMouse Společné vlastnosti přesunuty do CMouseBase CHouseMouse a CFieldMouse potomci CMouseBase 39 Principy OOP - Dědičnost, rozhraní 29.9.2014

Přetypování private/protected potomka Lze získat přístup k public metodám předka z instance potomka, který dědil pomocí private/protected pomocí jeho přetypování na předka? nelze, viz. brokenrightsdemo.cpp http://stackoverflow.com/questions/9661936/inher itance-a-is-an-inaccessible-base-of-b 40 Principy OOP - Dědičnost, rozhraní 29.9.2014

Přetypování private/protected potomka (2) class A { public: void foo() { cout << "foo called" << endl; } }; class B : protected A { }; int main() { A a; a.foo(); B b; //b.foo(); // error: 'void A::foo()' is inaccessible } // Let's try to retype B to A to get access to originally public method A& refb = b; // error: 'A' is an inaccessible base of 'B' refb.foo(); 41 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost a virtuální dědičnost Problém typu diamand vzniká typicky při nevhodné OO hierarchii Virtuální dědičnost umožňuje obejít, ale MNOHEM vhodnější je přímo odstranit problém změnou hierarchie (pokud je možné) CPerson CStudent CTeacher CTeachStudent 42 Principy OOP - Dědičnost, rozhraní 29.9.2014

Abstraktní třída 43 Principy OOP - Dědičnost, rozhraní 29.9.2014

Generalizace Cílem je navržení takového rozhraní, které pod sebe schová chování více typů objektů Hledají se společné vlastnosti různých objektů Např. iterátor na procházení pole není podstatné, že jde o int[] nebo float[] pole Např. zobrazení objektů na cílovou plochu není podstatné, jaká přesně bude (obrazovka, tiskárna) Např. tiskárny není podstatná technologie tisku Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Motivace pro rozhraní Chceme podchytit, co všechno musí splňovat třída, aby se mohla vydávat za příslušníka dané skupiny např. všechny grafické objekty je možné vykreslit V C++ implementujeme pomocí společného předka class IDrawable; požadavky na chování příslušníků zachytíme v jeho veřejných metodách např. virtual void paint(); Potomci si provádí vlastní implementaci těchto metod void CButton::paint() const {} (Společný předek nemusí mít smysl jako instance) Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Čistě virtuální metoda (pure virtual) Metoda, která má ve třídě pouze svou deklaraci implementace je ponechána na potomky Syntaxe virtual návratový_typ metoda(parametry) = 0; Potomci standardním způsobem implementují překrývají čistě virtuální metodu předka Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Abstraktní třída class CPersonInterface { public: virtual const char* getemail() = 0; virtual void print() = 0; }; Třída s alespoň jednou čistě virtuální metodou Nelze z ní přímo vytvářet instance (objekty) chyba při překladu Lze ale využít jako třídu pro dědění potomci překrývají virtuální metody abstraktního předka Analogie rozhraní v Javě může ale obsahovat implementaci některých funkcí Čistě abstraktní třída - všechny metody jsou čistě virtuální opravdové rozhraní ve stylu Javy Principy OOP - Dědičnost, rozhraní 29.9.2014

Abstraktní třída - obrázek Čistě abstraktní třída Principy OOP - Dědičnost, rozhraní 29.9.2014 Abstraktní třída (může být část implementace) Třída s implementací (děláme objekty) Principy OOP - Dědičnost, rozhraní 29.9.2014

Abstraktní třída - ukázka // Abstract class, at least // one method is pure virtual class IPerson { public: }; virtual const char* getemail() = 0; virtual void print() = 0; int main() { IPerson* person = new CStudent("novak@fi.muni.cz"); person->print(); delete person; return 0; } class CStudent : public IPerson { char m_email[max_email_length+1]; public: CStudent(const char* email) { strncpy(m_email, email, MAX_EMAIL_LENGTH); m_email[max_email_length] = 0; } virtual const char* getemail() { return m_email; } virtual void print() { cout << "Student: " << m_email << endl; } }; Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Abstraktní třída - ukázka abstractclassdemo.cpp Čistě virtuální metoda Abstraktní třída Čistě abstraktní třída Potomek implementující abstraktní třídu Potomek implementující jen část čistě virtuálních metod Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Psaní dobrého kódu (2) Označte virtuální ty metody, které budou v potomkovi definovat jeho specializaci SerializeIntoFile() kód pro manipulaci zápis do souboru bude v předkovi pro potomky virtuální funkce SerializeIntoBuffer() Nedělejte všechny metody virtuální pokud není třída zároveň čistě abstraktní (rozhraní) jinak svědčí spíš o špatném návrhu hierarchie Principy OOP - Dědičnost, rozhraní 29.9.2014 Principy OOP - Dědičnost, rozhraní 29.9.2014

Operátor reference & 52 Principy OOP - Dědičnost, rozhraní 29.9.2014

Reference na proměnnou Alternativní jméno pro objekt/proměnou (~alias) operátor reference je & int a = 1; int b = 5; int& reftoa1 = a; // First reference to a int& reftoa2 = a; // Another reference to a //int& uninitializedref; // error: uninitialized reference a += 9; operátor reftoa1 += 9; reference reftoa2 += 11; Určitá analogie s ukazatelem na proměnnou není ukazatel na objekt, ale má podobné použití není proměnná s adresou na původní 53 Principy OOP - Dědičnost, rozhraní 29.9.2014

Předávání argumentů referencí Alternativa k předávání argumentů funkce ukazatelem volání hodnotou: volání odkazem: volání referencí: void byvalue(int A) { A = 10; } void bypointer(int* pa) { *pa = 10; } void byreference(int& A) { A = 10; } Zavolání funkce vypadá jako volání hodnotou při předávání referencí není vytvářena kopie objektu změna argumentu uvnitř funkce se ale projeví i mimo funkci 54 Principy OOP - Dědičnost, rozhraní 29.9.2014

Konstantní reference Konstantní reference void foo(const typ& param) umožňuje specifikovat záměr programátoru o nakládání s objektem (referencí, ale nebude měněn) Kontrolováno během překladu konstatní reference na nekonstantní objekt - chyba změna nekonstantního objektu přes konstantní referenci - chyba void constreferencedemo(const int& A, const int* pb) { // We can read values cout << "a + b: " << A + *pb << endl; // But we can't change them A = 1; // error: assignment of read-only reference 'A' *pb = 1; // error: assignment of read-only location '* pb' } Principy OOP - Dědičnost, rozhraní 29.9.2014 55

Konstantní reference detaily Pro parametry v hlavičce funkce/metody platí, že konstantní reference je "catch-all" Oproti normální referenci, která umí chytat pouze lvalue (to, co někde bydlí, má to adresu), tak konstantní umí chytat všechno, i dočasné objekty Právě proto, že je konstantní reference "catchall", tak má při určování, která fce se zavolá (při stejné signatuře) nižší prioritu, než funkce pouze s referencí 56 Principy OOP - Dědičnost, rozhraní 29.9.2014

Reference - ukázka referencedemo.cpp více referencí na jedinou proměnnou nutnost inicializace reference předání argumentu referencí změna hodnoty mimo funkci konstantní reference 57 Principy OOP - Dědičnost, rozhraní 29.9.2014

Rozhraní a dědičnost Rozhraní a práce s ním je klíčový koncept Abstrakce od konkrétní implementace Tvorba generického kódu Snadná rozšiřitelnost později Vyžaduje dobře navrženou hierarchii Dědičnost umožňuje potomkovi vystupovat jako datový typ předka Konkrétní implementace za rozhraní Násobná dědičnost vs. Kompozice Ne vždy je vhodné dědit, zvažte vždy kompozici 58 Principy OOP - Dědičnost, rozhraní 29.9.2014

Shrnutí Už bylo 59 Principy OOP - Dědičnost, rozhraní 29.9.2014

Bonus Principy OOP - Dědičnost, rozhraní 29.9.2014

Principy OOP - Dědičnost, rozhraní 29.9.2014

Deadly sins of programming Zkušenost není pouze jak věci dělat, ale i to jak je nedělat Co ale s postupy, které jsou lákavé, tušíme kontroverzi, ale veříme, že právě my to zvládneme? Vítejte ve světě programátorských hříchů Rowan Atkinson na nás sice pozapomněl https://www.youtube.com/watch?v=91dsnl1beey Ale další už nikoli http://www.infoworld.com/d/developer-world/the-7-deadly-sinssoftware-development-872 http://msmvps.com/blogs/jon_skeet/archive/2006/06/10/deadlysins.aspx https://blogs.msdn.com/b/ericgu/archive/2006/08/03/687962.aspx http://www.cse.uaa.alaska.edu/~afkjm/cs470/handouts/securitysin s.pdf Principy OOP - Dědičnost, rozhraní 29.9.2014

Deadly sins of software development Lust (smilstvo) (overengineering) Gluttony (nestřídmost) (failing to refactor) Greed (lakomství) (competing across teams) Sloth (lenost) (not validating inputs) Wrath (hněv) (not commenting code) Envy (závist) (not using version control) Pride (pýcha) (not unit testing) http://www.infoworld.com/d/developer-world/the- 7-deadly-sins-software-development-872 Principy OOP - Dědičnost, rozhraní 29.9.2014

Principy OOP - Dědičnost, rozhraní 29.9.2014

Samostudium 65 Principy OOP - Dědičnost, rozhraní 29.9.2014

Problém diamant, virtuální dědičnost 66 Principy OOP - Dědičnost, rozhraní 29.9.2014

Problém typu diamant - motivace Třída CPerson string email, getemail() Třída pro studenta class CStudent: public CPerson; getemail() vrací formát xnovak@fi Třída pro učitele class CTeacher: public CPerson; getemail() vrací formát novak@fi Cvičící, ale také student CPerson CStudent CTeacher CTeachStudent class CTeachStudent: public CStudent, public CTeacher; Co vrátí volání getemail()? 67 Principy OOP - Dědičnost, rozhraní 29.9.2014

Problém typu diamant kde je problém? Nelze zkompilovat Nevhodné použití dědičnosti (Vyskytuje se často) 68 Principy OOP - Dědičnost, rozhraní 29.9.2014

Diamant ukázka #include <iostream> #include <string.h> using namespace std; #define MAX_EMAIL_LENGTH 30 class CPerson { protected: char m_email[max_email_length+1]; public: CPerson(const char* email) { strncpy(m_email, email, MAX_EMAIL_LENGTH); m_email[max_email_length] = 0; } const char* getemail() { return m_email; } }; int main() { CTeachStudent teachstud("novak@fi.muni.cz"); teachstud.getemail(); } return 0; class CStudent : public CPerson { public: CStudent(const char* email) : CPerson(email) {} }; class CTeacher : public CPerson { public: CTeacher(const char* email) : CPerson(email) {} }; class CTeachStudent : public CTeacher, public CStudent { public: CTeachStudent(const char* email) : CTeacher(email), CStudent(email) {} }; Principy OOP - Dědičnost, rozhraní 29.9.2014 69

Problém typu diamant Vzniká při nevhodném využití násobné dědičnosti Třída CTeachStudent zdědila jednu kopii metody getemail od CStudent a druhou od CTeacher Při volání metody getemail() třídy CTeachStudent není jasné, která kopie dat/metod se má použít getemail() z CStudent nebo CTeacher? Obdobný problém by vnikl ve funkci printemail() void CTeachStudent::printEmail() { cout << m_email; } 70 Principy OOP - Dědičnost, rozhraní 29.9.2014

Diamant plná kvalifikace Řešení pomocí plné kvalifikace jména metody/atributu teachstud.cteacher::getemail(); CTeacher::m_email; Je nutné znát hierarchii, porušuje myšlenku zapouzdření class CTeachStudent : public CTeacher, public CStudent { public: CTeachStudent(const char* email) : CTeacher(email), CStudent(email) {} void printemail() { //cout << m_email; cout << CTeacher::m_email; } }; int main() { CTeachStudent teachstud("novak@fi.muni.cz"); //teachstud.getemail(); teachstud.cteacher::getemail(); 71 } return 0; Principy OOP - Dědičnost, rozhraní 29.9.2014

Diamant - virtuální dědičnost Pomocí klíčového slova virtual přikážeme jedinou kopii atributů a tabulky metod používat opatrně class CStudent: virtual public CPerson; Problémem je, že už při deklaraci CStudent musíme tušit, že později nastane diamant Pokud se mixuje virtuální a nevirtuální dědičnost jen některá větev používá virtuální dědičnost tak stále vzniká více kopií 72 Principy OOP - Dědičnost, rozhraní 29.9.2014

Dědičnost a virtuální dědičnost Problém typu diamand vzniká typicky při nevhodné OO hierarchii Virtuální dědičnost umožňuje obejít, ale MNOHEM vhodnější je přímo odstranit problém změnou hierarchie (pokud je možné) 73 Principy OOP - Dědičnost, rozhraní 29.9.2014