2

I'm a c++ n00b and I'm not sure if I have looked in the right places but I'm confused about this:

include <iostream>

using namespace std;

class Enemy
{
    public:
        void sayHere()
        {
            cout<<"Here"<<endl;
        }
        virtual void attack()
        {
        }
};

class Monster: public Enemy
{

    public:
        virtual void attack()
        {
            cout<<"RAWR"<<endl;
        }

};

class Ninja: public Enemy
{

    public:
        virtual void attack()
        {

            cout<<"Hiya!"<<endl;
        }
};

int main()
{
    Ninja n;
    Monster m;
    Enemy enemies[2];
    enemies[0] = m;
    enemies[1] = n;

    for(int i = 0; i<2; i++)
    {
        enemies[i].sayHere();
        enemies[i].attack();//Will not be overriden
    }
    return 0;
}

My question is why isn't the attack() function in the Monster or Ninja class being overriden? Any help, even a link, would be greatly appreciated.

Hudson Worden
  • 2,263
  • 8
  • 30
  • 45
  • 3
    `public Enemy`? I must be hallucinating ... – Benjamin Bannier Jul 30 '11 at 05:00
  • for the record, this kind of mistake is called "slicing": https://en.wikipedia.org/wiki/Object_slicing. Basically, when you wrote enemies[0]=m; you tried to stuck a too large object (m, which is the derived) into a too small memory location (enemies[0], which is the base). C++ sliced ("cut") the object and put there only the first part of it, which is... the base class. Basically, you did only copy the base class into the array and all objects inside the array are of type Enemy. – user1284631 Sep 24 '14 at 17:43
  • mea culpa, was already told. – user1284631 Sep 24 '14 at 17:47

5 Answers5

5

Just do:

Enemy* n = new Ninja();
Enemy* m = new Monster();

n->sayHere();
n->attack();
m->sayHere();
m->attack();

delete n;
delete m;

That should do as you want. You need to use pointers for it to work. The reason lies in the way dynamic binding works.

Whenever a program has a C++ virtual function declared, a v-table is constructed for the class. The v-table consists of addresses to the virtual functions for classes and pointers to the functions from each of the objects of the derived class. Whenever there is a function call made to the c++ virtual function, the v-table is used to resolve to the function address. This is how the Dynamic binding happens during a virtual function call.

The idea is, the compiler stores pointers to each method based on the memory address of the object. It needs the pointer to access the table and invoke the appropriate function pointer. Think about if you wanted to write an OO version of C, how would you supply such a mechanism as inheritance and polymorphism? It makes sense when you think of it that way.

I just read that you are moving over from JAVA. In JAVA, most of your objects are stored on the heap. It is all just implied.

JAVA's

Enemy n = new Ninja();
n.attack();

is roughly equivalent to

Enemy* n = new Ninja();
n->attack();

Where the . operator in JAVA is more like the -> operator in c++. In both cases, n is on the heap. Java just hides all of the pointer and memory management stuff from you. This is why you can have blissful ignorance of the way dynamic binding works in JAVA and C#.

Jonathan Henson
  • 8,076
  • 3
  • 28
  • 52
4

This has to do with the fact that you don't access your enemies through pointers:

    Ninja n;
    Monster m;

    Enemy *enemies[2];

    enemies[0] = &m;
    enemies[1] = &n;

    for (int i = 0; i < 2; i++)
    {
        enemies[i]->sayHere();
        enemies[i]->attack();
    }
    return 0;
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • And I was watching TV at the same time. ;-) – Rudy Velthuis Jul 30 '11 at 05:05
  • Close but not quite right. It isn't if the object is on the heap or the stack but if you use a pointer or reference to the object. You can only get polymorphic behavior (i.e. virtual functions) if you use a pointer or reference. – Zan Lynx Jul 30 '11 at 05:05
  • The question is "why isn't the attack() function in the Monster or Ninja class being overriden?" – Murali VP Jul 30 '11 at 05:06
  • 1
    Thanks, that worked, but I'm confused about what the heap is. I come from java and this is all new to me. – Hudson Worden Jul 30 '11 at 05:10
  • I would have stored n and m as object. Then stored pointers to the object in the enemies array. You don't need to dynamically allocate them in-fact I would prefer not to unless you were using a container that owned the pointers (like boost::ptr_vector). – Martin York Jul 30 '11 at 05:15
  • @Zan, Martin: correct, that would have been possible too. I'll change the answer accordingly. – Rudy Velthuis Jul 30 '11 at 05:18
  • FWIW, totally off-topic, but what would someone think if he or she read: "This has to do with the fact that you don't access your enemies through pointers" out of context? – Rudy Velthuis Jul 30 '11 at 09:18
  • @Hudson: Read this http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap – Rudy Velthuis Jul 30 '11 at 11:21
2

Unless you access an object through a pointer or reference, virtual function calls will not work. To get it to work, you'll need to re-write your enemies array as

Enemy *enemies[2];
enemies[0] = &m;
enemies[1] = &n;

Note that you would have to change all enemies[i]. to enemies[i]->.

Daniel Gallagher
  • 6,915
  • 25
  • 31
1
Enemy enemies[2];   
enemies[0] = m;    
enemies[1] = n;

This is just object slicing - only Enemy object from derived objects will be copied. Virtual functions cannot play role.

Ajay
  • 18,086
  • 12
  • 59
  • 105
0

The phenomenon is called object slicing.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • 1
    Indeed. But if you read all the articles about object slicing, none of them seems to mention why that doesn't let you call virtual functions polymorphically. I know why, but an explanation to link to would be nice. – Rudy Velthuis Jul 30 '11 at 09:41
  • If an object is sliced, then of course you can't call virtual functions polymorphically because *you don't have an instance of the subclass any more*. That's what "slicing" means. This ought to be immediately apparent to anyone who actually understands and thinks about it. – Karl Knechtel Jul 30 '11 at 23:25
  • 1
    You and I know that, but I don't think it is that obvious. – Rudy Velthuis Jul 30 '11 at 23:36