C++ Polymorphism
-
- Posts: 30
- Joined: Fri Feb 01, 2013 12:06 pm
C++ Polymorphism
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.
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.
Re: C++ Polymorphism
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! )
Assume this:
So if you'd now do:
It outputs:
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.
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;
}
}
Code: Select all
Weapon w;
Item* item = &w;
item->whoAmI();
Code: Select all
"WEAPON!"
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);
Re: C++ Polymorphism
Polymorphism is what allows Irrlicht to work, basically
You use it through virtual methods.
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:
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):
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:
This is polymorphism. I hope you understand it.
You use it through virtual methods.
Code: Select all
virtual void foo();
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;
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;
};
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" :)
}
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
-
- Posts: 30
- Joined: Fri Feb 01, 2013 12:06 pm
Re: C++ Polymorphism
Thanks a lot,Mel and polylux.It is more understandable now
Re: C++ Polymorphism
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.
Re: C++ Polymorphism
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 )
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 )
Re: C++ Polymorphism
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.gerdb wrote:i think a small base class is always good, as you can iterate over them, independent of their actual implementation/behaviour.
Only create them if you really need them.
Working on game: Marrbles (Currently stopped).
Re: C++ Polymorphism
Or create diferent base classes where needed.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Re: C++ Polymorphism
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:
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:
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:
Of cause usage is fairly simple:
- adding to inventory items vector:
- and the processing: when user clicks with Shift button on the inventory item -- he wants to unstack:
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());
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
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;
}
};
- adding to inventory items vector:
Code: Select all
items.push_back(GoldenCoin());
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;
}