C++ heeft een inheritance mechanisme Manier om functionaliteit te ‘erfen’ van een parrent class ◦ Polymorphisme ◦ Zoals we het ook in C# kennen
class A { public: void doeDing() { cout<<"Ding!"<<endl; } }; class B : public A { };
int main() { A a; B b; a.doeDing(); b.doeDing(); return 0; } Output: Ding!
Net als in C# kunnen we functies uit de base class overriden Hiervoor moet een function in de base class met virtual aangeduid worden
class A { public: virtual void doeDing() { cout<<"Ding!"<<endl; } }; class B : public A { public: void doeDing() { cout<<“Nog Een Ding!"<<endl; } };
int main() { A a; B b; a.doeDing(); b.doeDing(); return 0; } Output: Ding! Nog Een Ding!
Let op bij overloading! Stel we overriden maar 1 variant van een virtual overloaded function: class A { public: virtual void doeDing() { cout<<"Ding!"<<endl;} virtual void doeDing(char* a) { cout<<a<<endl;} }; class B : public A { public: void doeDing() {cout<<"Dang!"<<endl;} };
En nu proberen we een string argument mee te geven bij een B object B b; b.doeDing("Bam!"); error C2660: 'B::doeDing' : function does not take 1 arguments
Voeg daarom expliciet de overloads toe aan je nieuwe class! class A { public: virtual void doeDing() { cout<<"Ding!"<<endl;} virtual void doeDing(char* a) { cout<<a<<endl;} }; class B : public A { public: void doeDing() {cout<<"Dang!"<<endl;} using A::doeDing; };
In C# kunnen zouden we een B object ook kunnen zien als een object met type A, maar... int main() { A a = B(); a.doeDing(); return 0; } Output: Ding!
Waarom krijgen we nu als output “Ding!” en niet “Nog Een Ding!” ? Alles in de variable ‘a’ wordt geinterpreteerd als een object van type ‘A’ ◦ Dit betekend dus ook dat we alle functies van A krijgen! Hoe lossen we dit op?
Bij polymorpishe objecten moeten we ‘indirect’ onze objecten benaderen om het gewenste gedrag te krijgen Dit lossen we op met pointers! ◦ Bij het dereferencen van de pointer wordt de juiste function pointer opgezocht
Output: Nog Een Ding! int main() { A *a = &B(); a->doeDing(); return 0; }
Stel we willen functies van de parent class ◦ Scope operator! class A { public: virtual void doeDing() { cout<<"Ding!"<<endl; } }; class B : public A { public: void doeDing() { A::doeDing(); } };
De constructor van de parent wordt automatisch aangeroepen ◦ (Voor de constructor van de Child) class A { public: A() { cout<<"ctor A"<<endl; } }; class B : public A { public: B() { cout<<"ctor B"<<endl; } }; B b; Output: ctor A ctor B
Als er geen default constructor is, hoe komen we dan aan de argumenten? ◦ Child constructor moet parent constructor aanroepen met correcte argumenten! class A { public: A(char *a) { cout<<a<<endl; } }; class B : public A { public: B() : A("bam!") { } }; B b; Output: bam!
Als we een B aanmaken, wordt ook het A deel aangemaakt: dit deel moet ook weer verwijderd worden! ◦ Automagisch.. class A { public: ~A() { cout<<"dtor A"<<endl; } }; class B : public A { public: ~B() { cout<<"dtor B"<<endl; } }; { B b; } Output: dtor B dtor A
Maar als we B als een A zien, gaat dat niet zo automagisch! Output: dtor A int main() { A *a = new B(); delete a; return 0; }
Heel belangrijk: Maak ook je destructor virtual! class A { public: virtual ~A() { cout<<"dtor A"<<endl; }}; class B : public A { public: ~B() { cout<<"dtor B"<<endl; } }; A *a = new B(); delete a; Output: dtor B dtor A
Ook in C++ hebben we abstract classes Een class is abstract als tenminste één methode een pure virtual fuction is ◦ Virtual met een twist! Net als in C# kunnen we abstracte classes niet instancieren
Een virtual function zonder implementatie (expliciet!) ◦ Een child class moet de virtual function implementeren, anders is ook de child abstract class A { public: virtual void doeDing() = 0; }; class B : public A { public: void doeDing() {... } };
We kunnen geen objecten van een abstracte class instancieren We kunnen wel het pointer type van de class gebruiken om naar child types te wijzen A *a = new B();
Denk eraan, in C++ is alles pass-by-value! ◦ Stel, we maken een vector aan ◦ Binnen de vector wordt ruimte gemaakt voor A objecten ◦ Stel dat we nu een B object in deze vector gooien ◦ Stel nu ook dat B meer data bevat dan A (bijvoorbeeld extra member variables) ◦ Alleen sizeof(A) aan data word opgeslagen! Het B object word ergens afgekapt (Slicing) !
Om Slicing op te lossen moeten we dus de variable loskoppelen van de grootte van het object Pointers! ◦ In het vorige voorbeeld hadden we dus een vector moeten gebruiken
Polymorphisme werkt hand in hand met pointers Bij het aanmaken van een polymorpisch object wordt ook de zogenaamde ‘virtual table’ aangemaakt De ‘virtual table’ bevat de pointers naar de correcte functions Als een methode wordt aangeroepen wordt de correcte functie uit de ‘virtual table’ opgehaald