Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behaviour of the `virtual` keyword in C++ on objects instead of pointers

Tags:

c++

virtual

I was trying to understand virtual functions.

Consider the following code,

#include <iostream>
#include <memory>
#include <vector>

class Animal 
{
public:
     virtual void eat() 
    {
        std::cout << "I eat like a generic animal.\n";
    }

};

class Wolf : public Animal 
{
public:
    void eat() 
    {
        std::cout << "I eat like a wolf!\n";
    }
};


int main() 
{

  Animal      a;
  Wolf        w;

  a.eat();
  w.eat();

}

With the virtual keyword I get the output

I eat like a generic animal.
I eat like a wolf!

as it should.

But If I remove the virtual keyword I still get the same output! From my elementary understanding of virtual functions, without the virtual I should have got the output

I eat like a generic animal.
I eat like a generic animal.

Is there anything elementary here I am missing ?

I am using the g++ compiler on Linux

like image 287
smilingbuddha Avatar asked Dec 26 '22 21:12

smilingbuddha


2 Answers

No, it's a right behavior. Virtual functions are needed to introduce polymorphism. To enable polymorph behavior, you need to use pointers like this:

 Animal * a = new Animal();
 Animal * w = new Wolf();

 a->eat();
 w->eat();

 <...>

 delete a;
 delete w;

Provided the way you have it now, the behavior is right, because both variables clearly have different types.

like image 149
SingerOfTheFall Avatar answered Jan 27 '23 03:01

SingerOfTheFall


Polymorphism works by identifying the type of object that an instance actually refers to.

In your case, your actual animals are as follows:

Animal      a;  //a is an animal.
Wolf        w;  //w is a wolf.

So, you're not using polymorphism at all.

What you need to do is more like this:

//Create a couple animal pointers.
Animal* a;
Animal* b;

//Create an animal instance and have a point to it.
a = new Animal();

//Create a wolf instance and have b point to it.
b = new Wolf();

//Calls Animal::eat as a is an animal.
a->eat();

//Calls Wolf::eat as a is a wolf.
b->eat();

Note that you can use pointers or references to achieve this use of polymorphism.

That is why you should usually pass objects by const-reference when working with class types.

//Will call Animal::eat or Wolf::eat depending on what animal was created as.
void foo(const Animal& animal) {
    animal.eat();
}

//Will always call Animal::eat and never Wolf::eat since this isn't a reference or
//a pointer.  Will also "slice" a Wolf object.
void foo(Animal animal) {
    animal.eat();
}

Note that slicing means it will turn a more derived class (wolf) into a less derived copy of that class (animal) indiscriminately which can be very misleading and unexpected.

like image 30
John Humphreys Avatar answered Jan 27 '23 04:01

John Humphreys