Hudson Worden
Hudson Worden

Reputation: 2353

Confused about Virtual Functions C++

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.

Upvotes: 2

Views: 1091

Answers (5)

Karl Knechtel
Karl Knechtel

Reputation: 61478

The phenomenon is called object slicing.

Upvotes: 0

Jonathan Henson
Jonathan Henson

Reputation: 8206

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#.

Upvotes: 5

Daniel Gallagher
Daniel Gallagher

Reputation: 7095

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]->.

Upvotes: 2

Rudy Velthuis
Rudy Velthuis

Reputation: 28806

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;

Upvotes: 4

Ajay
Ajay

Reputation: 18411

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.

Upvotes: 1

Related Questions