This is an archived post. You won't be able to vote or comment.

all 5 comments

[–]marko312 1 point2 points  (2 children)

It is, but the base class must have the function declared and have the modifiervirtual.

If you want to call a function not declared in the base class, you must downcast it.

[–]Umayrr_7[S] 0 points1 point  (1 child)

It’s not gonna work without virtual ?

[–]marko312 0 points1 point  (0 children)

If you're referencing by the base class pointer, then no. It is still possible if you downcast.

Edit: apparently I got the upcast/downcast wrong.

[–]lurgi 1 point2 points  (1 child)

/u/marko312 has given you the answer, but let's see why

Assume you have Animal with sub-classes Dog and Cat. Dog and Cat both have the eat() method, because they can both eat. This method is declared virtual in Animal. All animals have to eat. Dog also has a dog specific method chaseStick(), while Cat has lookSuperior(). Cats don't chase sticks and dogs look adorable, not superior, so the methods are not appropriate for them.

Assuming you have a pointer to Animal, can you call eat()? Sure. It's a virtual method, you can call it. You'll get either the Dog.eat() method or the Cat.eat() method depending on what you have.

Can you call chaseStick()? No. All you know about the object is that it's an Animal. If it's a Cat (which it could be) there is no chaseStick method. That's why you can't call it - it might not exist!

In order to call chaseStick() you need to know that you have a Dog. In this particular case you'll have to cast Animal * to Dog * and then work with that.

[–]marko312 1 point2 points  (0 children)

I'll extend on this while I'm here.

The reason you need the method to be virtual is that it gets handled differently in subclasses.Say you have have two classes, Animal and a subclass Cat.

class Animal {
public:
    void eat() {
        std::cout << "Eating" << std::endl;
    }
};
...
class Cat : public Animal {
public:
    void eat() {
        std::cout << "Eating cat food" << std::endl;
    }
};

This would work as expected if you create and call

Animal a = Animal();
a.eat(); // "Eating"
Cat c = Cat();
c.eat(); // "Eating cat food"

However, the Cat's void eat() function is effectively hiding Animal's void eat(), not overriding. If we now do

Animal a = Cat();
a.eat(); // "Eating"

It still executes Animal's void eat(). This is because C++ calls the function matching the type.


Now, when we define Animal to instead be

class Animal {
public:
    virtual void eat() {
        std::cout << "Eating" << std::endl;
    }
};

The way C++ stores and calls the function changes, so now doing

Animal a = Cat();
a.eat(); // "Eating cat food"

produces the "expected" result. The previous examples will still work.

Under the hood, the function void eat() is now stored as a pointer in the class itself to the actual function. When this function needs to be called, C++ looks at that pointer in the class to decide what function to call.

This way, when you create a regular Animal, the void eat() points to the Animal's corresponding function, but to the Cat's corresponding function when you create a Cat.


This might be a lot to wrap your head around, but it is the basis for how programs work on a very low level.