1

I'm currently working on the examples from "C++ through Game Programming" and I came to this example that proves polymorphism

#include <iostream>

using namespace std;

class Enemy
{
public:
    Enemy(int damage = 10);
    virtual ~Enemy();
    void virtual Attack() const;

protected:
    int* m_pDamage;
};

Enemy::Enemy(int damage)
{
    m_pDamage = new int(damage);
}

Enemy::~Enemy()               
{
    cout << "In Enemy destructor, deleting m_pDamage.\n";
    delete m_pDamage;
    m_pDamage = 0;
}

void Enemy::Attack() const
{
    cout << "An enemy attacks and inflicts " << *m_pDamage << " damage points.";
}  

class Boss : public Enemy
{
public:
    Boss(int multiplier = 3); 
    virtual ~Boss();
    void virtual Attack() const;

protected:
    int* m_pMultiplier; 
};

Boss::Boss(int multiplier)
{
    m_pMultiplier = new int(multiplier);
}

Boss::~Boss()                 
{
    cout << "In Boss destructor, deleting m_pMultiplier.\n";
    delete m_pMultiplier;
    m_pMultiplier = 0;
} 

void Boss::Attack() const
{
    cout << "A boss attacks and inflicts " << (*m_pDamage) * (*m_pMultiplier)
         << " damage points.";
} 

int main()
{
    cout << "Calling Attack() on Boss object through pointer to Enemy:\n";
    Enemy* pBadGuy = new Boss();
    pBadGuy->Attack();

    cout << "\n\nDeleting pointer to Enemy:\n";
    delete pBadGuy;
    pBadGuy = 0;

    return 0;
}

My question is, why this line was used:

Enemy* pBadGuy = new Boss()

instead of

Boss badGuy;
badGuy.Attack();

?

The author calls it "Using babes Class Pointers to Derived Class Objects". Is it used often ? Does it has any advantages in respect to the "normal" instantiate method ?

Thanks!

Sabyc90
  • 123
  • 2
  • 10
  • Possible duplicate of [Why should I use a pointer rather than the object itself?](http://stackoverflow.com/questions/22146094/why-should-i-use-a-pointer-rather-than-the-object-itself) – Revolver_Ocelot Dec 19 '15 at 18:48
  • Do yourself a favor and find a different book to learn C++ programming. This code example has many things wrong with it that you'll just have to unlearn. Like `using namespace std;`, those `int*`'s that point to a single `int` (could have just used an `int`), assigning `0` to a pointer just before it goes away (waste of time); all those pointlessly protected members. – Pete Becker Dec 19 '15 at 19:05
  • I see your point and I've been reading through other books as well . I don't really understand why assigning 0 to a pointer. I think this book explains some concepts (like classes, vectors, pointers and references) better than others. I was reading Prata also but it's gets so detailed in a lot of places and kinda looses focus from the main subject. – Sabyc90 Dec 19 '15 at 19:14
  • 1
    Sorry, that was a little harsh. As long as you're reading other sources as well, and taking this one with several grains of salt, you should be okay. – Pete Becker Dec 19 '15 at 19:41

4 Answers4

2

This gives an example of how virtual methods work. Even though you're invoking a base class's method, it is the subclass's method that gets invoked.

Your proposed alternative does not clearly demonstrate this key concept.

Both alternatives accomplish the same thing, but this is meant to give an example of virtual method dispatch.

Sam Varshavchik
  • 84,126
  • 5
  • 57
  • 106
0

Think about the following: you have a hammers and saws. They're both tools. They are in bags (like vectors), and if you want to use them, you pull out one from the bag, and then you use it(call the object's method). You cannot put hammers in saws' bag and vice-versa, but you can put them in a bag called tools. It's not easier to have one bag instead of two. If you want to use them, you use it in an appropiate way (call the virtual method). So your enemies can be processed easier with polymorphism then without. Also, you can create methods, like

 // in your class...
 virtual void attackedByOther(const Enemy& other);

In this case you only need one function implemented more times, which does the damage calculation, etc.

So answering your question: if you use pointers you can do polymorphism, but with instanced you can't.

I hope you understand!

Lasoloz
  • 252
  • 3
  • 11
0

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.

bigcodeszzer
  • 854
  • 6
  • 20
0

I am just going to elaborate on what @SamVarshavchik said, and answer your questions in a sequential manner.

  1. This particular declaration has been used as this helps us utilize the concept of dynamic binding and use of virtual functions. The details on each of these can be found online. To tell in short, dynamic binding is the concept when the object is bound to the function at run-time and not during compile time, and virtual functions are those that support dynamic binding. Moreover dynamic binding can only be performed with pointers and references and not with objects themselves and since pointer to base class can be used as a pointer to derived class we use the given declaration.

  2. "Is it used often?" Yes, it is used very often, more so whenever polymorphism and the concept of inheritance is involved as it is not possible every time to know beforehand for which class the user intended to call the respective function.

  3. I think by now I have already answered your last question as I said since dynamic binding can be performed either with pointers or reference to base class and not with object we need to use the given method.

incomplet_
  • 178
  • 1
  • 9
  • Thanks for this answer also! The thing is that many books and online examples say "references can also be used" but there close to none examples of using references instead of pointers. – Sabyc90 Dec 20 '15 at 05:30