C++ Polymorphism

Discussion about everything. New games, 3d math, development tips...
Post Reply
RespectIsEverything
Posts: 30
Joined: Fri Feb 01, 2013 12:06 pm

C++ Polymorphism

Post by RespectIsEverything »

Hello everybody,
I try to make Item class.I want use this class in Weapon,Armor,Potion...etc.I searched and i found Polymorphism.I couldnt understand.Is there anybody knows Polymorphism?If anyone tells me BASICALLY and write an example,i will be very happy.
polylux
Posts: 267
Joined: Thu Aug 27, 2009 12:39 pm
Location: EU

Re: C++ Polymorphism

Post by polylux »

The baseline is that deriving classes can be called in the fashion of their base though they still act the 'right' way. (WHAT AN EXPLANATION! :D )

Assume this:

Code: Select all

class Item
{
    (...)
    virtual void whoAmI()
    {
        std::cout << "ITEM!" << std::endl;
    }
}
 
class Weapon : public Item
{
    (...)
    virtual void whoAmI()
    {
        std::cout << "WEAPON!" << std::endl;
    }
}
So if you'd now do:

Code: Select all

Weapon w;
Item* item = &w;
item->whoAmI();
It outputs:

Code: Select all

"WEAPON!"
It'll call Weapon's "whoAmI()" despite you call it as an object of class "Item".
Note that you have to declare it as "virtual" in the base class to tell the compiler not to statically call that function. (See "vtables" for further info)

This comes in handy e.g. if you have a general container (e.g. list, array) to store all your 'interact-able' entities, each of which is from a separate derivate.
beer->setMotivationCallback(this);
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: C++ Polymorphism

Post by Mel »

Polymorphism is what allows Irrlicht to work, basically :)

You use it through virtual methods.

Code: Select all

virtual void foo();
These are necesary because a virtual method will be linked in runtime, instead of statically linked during the compilation time.

If the class had no subclasses, this dynamic linkage would still be done, as useless as it maybe because there is only one definition of the method, if there was no definition, it would give a compilation error. Don't mistake this with DLL's. Stand alone programs still may make use of this kind of linkage.

Then, if the class has derived subclasses, the linked method will be only that of the class that calls it. A pointer to a parent class will give you the method of the parent class, and a pointer to the child class will give you the child's implementation. So far, so good.

And now comes the important part. If ANY of the methods of the parent class is defined as abstract, that is, a virtual method equaled to 0, like this:

Code: Select all

virtual void foo() = 0;
the class is abstract, and hence, is considered that the called methods of this class will be always those of the child classes if they are implemented. And any derived class MUST implement this foo method. or else, the dynamic linkage would fail, but this would result just in a compilation failure. This is the polymorphism.

It means that through a pointer to a base class, you get the implementation of the child class, which means that any class derived from the base class may behave just like the base class, and this way, you may unify the behavior of your objects. All act using the same naming convention, but with diferent implementations.

Take a look at this, this is a working example, i use it (more elaborately... but i use it):

Code: Select all

 
class executionUnit
{
//constructor... nothing in it
executionUnit() {}
 
virtual ~executionUnit() {} 
//it is important to define this virtual even if it is empty, because the destructors have special rules with regard to polymorphism
//they are called in cascade from child to parent class, instead of only that of the child class.
 
virtual bool init() = 0;
 
virtual bool run() = 0;
 
virtual void clean() = 0;
 
};
 
this class is abstract, and completely empty...

Now, in my program, i have several classes defined under that base class.

main, screen1, screen2...

well, each of those classes, main, screen1, screen2, implement the init, run and clean methods. Then, in my main i can do this:

Code: Select all

 
void main()
{
int i = 0;
executionUnit* unit;
//...i create the main,screen1 and screen2 objects...
 
main->init(); //safe initialization ;)
unit = main; //It is a legal assignment, unit is of the base class of main.
//from now on, we care little of what our unit pointer is aiming to... all of the child classes work the same, isn't it?
 
while(unit->run())
{
unit->clean();
 
switch(i%3)
{
case 0:
unit = main;
break;
 
case 1:
unit = screen1;
break;
 
case 2:
unit = screen2;
break;
}
 
unit->init();
++i;
}
//This ends when the unit class returns false in the run call... 
//See how we have created a program that runs 3 small "execution units" (hence the name) in "parallel" :)
 
}
 
This is polymorphism. I hope you understand it.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
RespectIsEverything
Posts: 30
Joined: Fri Feb 01, 2013 12:06 pm

Re: C++ Polymorphism

Post by RespectIsEverything »

Thanks a lot,Mel and polylux.It is more understandable now :D
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Re: C++ Polymorphism

Post by sudi »

actually for items i would not suggest that...but then again i dunno what kind of items you need. if it is an fps this is probably just fine. but for an rpg with an imense amount of items in the world all in different states and shapes, using polymophism is a bad idea to start from.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
gerdb
Posts: 194
Joined: Wed Dec 02, 2009 8:21 pm
Location: Dresden, Germany

Re: C++ Polymorphism

Post by gerdb »

i think a small base class is always good, as you can iterate over them, independent of their actual implementation/behaviour.

thatswhy java has a base class for everything called "class Object", everything else is derived from it and has to implement some methods like toString().

This way objects can be serialized, garbage collected, compared, etc...

For a item class, look what all items have in common, like getBoundingBox, isVisible, getName (toString), read/write(IXMLWriter* pWriter) or something like.

Of course there is much more work todo when the interface changes, because all derived classes have to be corrected as well, so think really carefully about common properties / methods.

By the way polymorphism is not only for classes, but for functions as well, as you can write functions with the same name but different parameters (different signatures). so

add( ObjectA& other ) is different from add( ObjectB& other ) but has the same name, this is not possible in native C, there you have to write 2 functions with different names

addA( ObjectA* other ) and addB( ObjectB* other )
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Re: C++ Polymorphism

Post by serengeor »

gerdb wrote:i think a small base class is always good, as you can iterate over them, independent of their actual implementation/behaviour.
I used base class for most of things before, it's a pain in the ass to maintain them when you need to change something.
Only create them if you really need them.
Working on game: Marrbles (Currently stopped).
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: C++ Polymorphism

Post by Mel »

Or create diferent base classes where needed.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Re: C++ Polymorphism

Post by greenya »

I will try to show polymorphism on example.
Lets say you want to make an inventory browser for you game.
Something like http://tera-cdn1.enmasse-game.com/datas ... entory.jpg
They said inventory must be a grid, each item in single slot, each item has own icon and own tooltip; the format of the tooltip will depend on type of item (e.g. no Attack Speed for helmets).

I made next code example:

Code: Select all

class InventoryItem {
    std::string title;
public:
    InventoryItem(std::string title) : title(title) {}
    virtual void drawIcon(int x, int y); // no default icon
    virtual void drawTooltip(int x, int y); // no default tooltip
};
 
class BronzeHelmet : public InventoryItem {
public:
    BronzeHelmet() : InventoryItem("Bronze Helmet") {}
    virtual void drawIcon(int x, int y) { /* ... */ }
    virtual void drawTooltip(int x, int y) { /* ... */ }
};
 
class SilverBlade : public InventoryItem {
public:
    SilverBlade() : InventoryItem("Silver Blade") {}
    virtual void drawIcon(int x, int y) { /* ... */ }
    virtual void drawTooltip(int x, int y) { /* ... */ }
};
 
int main(int argc, char* argv[])
{
    std::vector<InventoryItem> items;
    //items.push_back(InventoryItem()); // this is invalid, since InventoryItem has at least one pure virtual method
    // (and this is just what we need: we shouldn't allow adding pure InventoryItem -- it is too abstact; and OOP helps us here -- we can add only inheriters)
    items.push_back(BronzeHelmet());
    items.push_back(SilverBlade());
Imagine that you have like 100+ inheritors for InventoryItem. But the code which uses the inventory works with base class only, it calls base methods which are virtual and implemented in each inherited class in its own way. When user move the mouse over some inventory item, you just find it in your "items" vector and call something like "items[foundIndex].drawTooltip(mouseX, mouseY)" and it will call proper implementation automatically.

Next day they said you need to implement "stacking" feature. Its like when some certain items (not all) can be stored in single inventory cell. (on the image referenced above -- it is items with numbers on their icons).
So you simple add to InventoryItem class next:

Code: Select all

virtual bool stackable() { return false; } // by default item is not stackable
virtual InventoryItem* getstack(int count) { throw new std::exception(); } // by default it is illegal to call this method since item is not stackable
This doesn't break any code. This does nothing for now, you just declare that items that can be stackable shoud implement proper methods. So for example:

Code: Select all

class GoldenCoin : public InventoryItem {
    int stackcount;
public:
    GoldenCoin() : InventoryItem("Golden Coin"), stackcount(20) {}
    virtual void drawIcon(int x, int y) { /* ... */ }
    virtual void drawTooltip(int x, int y) { /* ... */ }
    virtual bool stackable() { return true; }
    virtual InventoryItem* getstack(int count) {
        if (count > stackcount + 1) throw new std::exception(); // we cannot return more than we have, and we cannot left less than 1 after all
        GoldenCoin* c = new GoldenCoin();
        c->stackcount = count;
        stackcount -= count;
        return c;
    }
};
Of cause usage is fairly simple:
- adding to inventory items vector:

Code: Select all

items.push_back(GoldenCoin());
- and the processing: when user clicks with Shift button on the inventory item -- he wants to unstack:

Code: Select all

    if (items[i].stackable()) {
        int userUnstackCountDesire = 5; // here we should ask how many items he want... lets say we know that now for simplicity        
        InventoryItem* j = items[i].getstack(userUnstackCountDesire);
        // now we need to attach j to the mouse pointer so user can place stack where he want it to be
    } else {
        // this InventoryItem doesn't support stacking
        // lets be patient to user's random shift+clicks and instead of throwing an exception here, we just ignore it :)
        return;
    }
Post Reply