Just to add a little to what the other people have said...
One of the main reasons to use abstract classes and virtuals, is so that you can have multiple object types in the same array.
Here's an example from a project I did in school:
char buffer = '\0';
int itemindex = 0;
Item* myitem;//.................................. I used a polymorphic pointer Item
while (!myfile.get(buffer).fail()){//............ Check for failure each loop iteration
if (buffer == 'N' || buffer == 'P') {
if (_noOfItems - 1 >= itemindex) {//.... -1 because of index 0 and >= to account for the first set of entries
delete _items[itemindex];//.......... if it is >= than there has already been allocation at that index, so it must be freed
}//...................................... to avoid memory leak
if (buffer == 'P') {
myitem = new Perishable();//......... polymorphic pointer static type is now perishable (dynamic will always be item)
} else if (buffer == 'N') {//............... else is extra safe
myitem = new NonPerishable();//....... polymorphic pointer static type is now nonPerishable (dynamic is item)
}
myfile.ignore();//....................... ignore comma
myitem->load(myfile);
_items[itemindex] = myitem;//............ This line assigns myitem to the item index, since its polymorphic, only have to write
//............. it once, within the 'N' || 'P' scope.
itemindex++;//........................... This must incriment every time 'N' || 'P' is encountered, cause each represents an
}//.......................................... item entry.
}
What we had to do was create Perishable and Nonperishable items. If you follow the comments, you will see I created an Item (base class) pointer, then based on the file that's being read, if the char is 'P' I create a Perishable object (derived) or NonPerishable object (also derived).
So the point here, is that _items[itemindex] = myitem;
is only called once, rather than in each condition branch buffer = P/N
There's a few interesting things going on in this example, but as I mentioned, both Perishable (child) and NonPerishable (child) are in the Item (parent) array.
So if we were dealing with bosses (child) and characters (child) and they were both entities (parent), you could loop through your entity (parent) array containing both derived types, and call the same function... something like this.
for(int i = 0; i < entityList.length; i++){
entityList[i].attack
}
Now that is /really/ cool. You can tell all of the entities to do the same thing, in one line, all because they are have the same parent type.
What you really need to know though, is what was already mentioned about dynamic dispatch. The way this was explained to me is simple: An object can have a dynamic type and a static type.
The dynamic type is the type the reference was created with, like this:
Item* myitem //Dynamic type is Item
The static type is what the pointer /currently/ points to. So right now the static type of myitem is also type Item.
If I do this:
myitem = new NonPerishable();
myitem pointer is now pointing to a child type 'NonPerishable'. The dynamic type doesn't change, because it was created as an Item. So dynamic type is /still/ type Item. The static type is however now a NonPerishable, because the Item pointer (myitem) is now pointing to a NonPerishable object.
Note: Dynamic is what it was created as (this may be counter intuitive in the name, but it is the case)
Lastly, if you have a parent class and child class that both have a function with the same name but different implementation you will either get early binding or late binding (aka dynamic dispatch).
Early binding means that the function that fires will be the parent function, dynamic dispatch means the function that fires will be the child function. Early binding is the default for C++, to get dynamic dispatch you must declare the function as 'virtual', then the default will be the child function.
One way you can override binding is by explicitly declaring the namespace. Here is an example:
class Parent; //pseudo code
class Child : public Parent
object.Child::myfunction()
Note: Normally, if Parent and Child have the same function (myfunction) it will be early binding (the parent version). In this case, you use the child:: namespace so it will call the child version of myfunction.
It's a lot of information, if you have any questions I can elaborate.