Polymorphism

Een van my gunsteling Object Oriented Design/Programming eienskappe is Polymorphism. Ek het dit in die verlede heelwat in C# gebruik (sonder om te besef dat dit Polymorphism is) en dit het die lewe aansienlik makliker gemaak en my kode baie meer leesbaar. Met die skryf van ons games en Pong kon ek dit nie lekker regkry in die begin met C++ nie, maar het dit weer probeer vir Space Invaders en toe my fout gevind. Gedink dit mag dalk vir julle ook handig wees en is tyd vir my om bietjie iets te post ook 🙂

So hoekom kan dit handig wees in ons games? Wel in die struktuur wat ek volg vir Space Invaders het ek net een container wat alle objects (behalwe nou ligte en sulke goed) in die game hou. Ek het n vector container gekies omdat sy overheads laer is as van die ander container tipes en hy sy elemente opeenvolgend in memory stoor (word later belangrik vir optimalisering). Die rede hoekom ek alles in een container wou hê kom in by funksies soos Render, CollisionDetection, ens. Dit is dan makliker om net daai een container na daai funksies te pass as om n Players, Aliens, Bullets, ens. containers na hulle te pass. Verder as mens inherited classes het dan kan ek net een funksie roep op elke element in die container, byvoorbeeld:

gameObjects[i]->Update();

Elke object weet dan presies wat om te doen (of nie) met die Update() funksie en dan is alles ge-update. Soos die prentjie hierbo die inskrywing mooi illustreer.

So die probleem wat nou onstaan is hoe kry n mens een container wat alle objects kan hou, as ek se vector<player> dan kan daai vector net player objects hou! Dit is dan nou hier waar Polymorphism in die storie kom. Hoe dit basies werk is dat as jy n class skep en n object van daai class maak, dan kan jy tegnies daai object gelyk stel aan objects van tipes wat inherit van die oorspronklike class. So m.a.w as jy n class skep met die naam “Vrugte” en dan nog een wat inherit van “Vrugte” met die naam “Appel” dan kan jy n object van tipe vrugte skep maar hom gelyk stel aan “Appel”. So iets:

class Vrugte
{
public:
    void Groei()
    {
    };
};

class Appel : public Vrugte
{
};

void main()
{
    Vrugte myVrugte;
    Appel myAppel;    

    // Stel myAppel wat van tipe Appel is gelyk aan myVrugte wat van tipe Vrugte is
    myVrugte = myAppel;
    
    myVrugte.Groei();
}

En dit werk (om twee verskillende tipes aanmekaar gelyk te stel) omdat Appel van Vrugte inherit. Verder kry Appel mos nou natuurlik ook die Groei() funksie wat hy inherit het. Wanneer mens nou byvoorbeeld Groei() op myVrugte roep dan word die kode wat binne die Groei() funksie is geroep. Maar die Groei() funksie binne die Vrugte class is generic en nie alle vrugte groei dieselfde nie. Hoe kan ek vir Appel en watter ander vrugte ek ook later byvoeg hulle eie Groei() funksie gee.

Dit was presies hier waar ek moed opgegee het met dit in Pong want in C# kon ek net Vrugte verklaar as n Abstract class en kon dan die funksies override in my inherited classes. Het te gou moed opgegee want in C++ kan mens basies dieselfde doen.

Die probleem is omdat mens hierdie objects op die stack verklaar word hulle memory tydens compile time toegeken. So daar word net genoeg memory vir n Vrugte tipe gehou alhoewel jy Appel aan hom kan gelykstel word al die extra funksies wat jy aan Appel gee gestroop en net die wat ooreenstem met Vrugte word gestoor.

Een oplossing is om funksie pointers te gebruik binne die funksies van jou base class. Byvoorbeeld om n funksie pointer binne die Groei() funksie van Vrugte te he. Na jy dan jou Vrugte object (myVrugte = myAppel;) geskep het point jy die funksie pointer na n funksie wat jy wil he geroep moet word wanneer Groei() geroep word. Dit werk en my eerste weergawe van Space Invaders het so gehardloop.
Maar C++ maak dit makliker met die Virtual keyword wat mens kan gebruik wanneer jy n funksie binne jou base class (Vrugte) verklaar. Wat Virtual doen is basies dieselfde as daai eerste metode van my, agter die skerms maak hy die funksie eindlik n funksie pointer. So wanneer jy n nuwe class skep wat inherit van n base class wat Virtual funksies het dan is dit eindlik pointers na daai funksies. Dit beteken dat as jy n Appel object gelyk stel aan n Vrugte object dan word die funksies nie konkreet in memory gestoor nie maar eerder pointers na daai funksies. Die enigste vereiste vir dit om te werk is dat jou object dan nie meer op die stack gestoor kan word nie.

So om dit nou te doen vir die vrugte voorbeeld:

class Vrugte
{
public:
    virtual void Groei()    // Verklaar met virtual
    {
    };
};

class Appel : public Vrugte
{
    void Groei()        // Appel se eie Groei() kode
    {
    }
};

void main()
{
    Vrugte *myVrugte = new Appel;        // Skep Vrugte object op heap
    
    myVrugte->Groei();                              // Roep nou appel se Groei() en nie vrugte sin nie

    delete(myVrugte);                                // Free die memory
}

En dit is basies hoe Polymorphism in C++ gedoen kan word. Ek is seker daar is nog baie ander tricks en ways maar hierdie werk vir my.

Hier is n link na nog n goeie verduideliking:
https://www.tutorialspoint.com/cplusplus/cpp_polymorphism.htm

0 thoughts on “Polymorphism”

  1. Ek het hierdie ook vir my Pong gebruik, maar het toe halfpad deur ‘n meer effektiewe metode gevind wat mooi vir daardie program gewerk het en inheritance and polymorphism net onnodig gemaak het. El wil net wel vir nie nuwe program gebruik, maar het tot op hede dit nie nodig gehad.

    Dis goed jy noem van die pointer. My idee met voorwerp is dat die modelklas ‘n updateStates virtual funksie het, maar ek wil hierdie amper eers by compile tyd define. Ek weet nog nie of dit moontlik is vir hoe ek dit wil gebruik, maar as ek die ponter gebruik mag dit werk. Basies elke vyand gaan moontlik sy eie beweeging kan hê, maar die basiese vyand-model gaan dieselfde wees.

  2. Okay cool, so jy dink ook daaraan om dit te gebruik. Dit werk op die oomblik vir my bitter nice en voel na n mooi skoon manier om jou game objects te manage. Maak dit ook maklik om nuwe tipe objects in die toekoms by te kan voeg. Dalk nog bietjie van n overkill vir basic Space Invaders maar dit los vir n mens spasie om die game te kan groei.

    Met hierdie metode kan jou vyande ook elkeen sy eie states he want elke vyand is mos net n instance van jou vyand class.

Leave a Reply to swanepoeljan Cancel reply