Overriding vs Virtual

40,896

Solution 1

This is the classic question of how polymorphism works I think. The main idea is that you want to abstract the specific type for each object. In other words: You want to be able to call the Child instances without knowing it's a child!

Here is an example: Assuming you have class "Child" and class "Child2" and "Child3" you want to be able to refer to them through their base class (Parent).

Parent* parents[3];
parents[0] = new Child();
parents[1] = new Child2();
parents[2] = new Child3();

for (int i=0; i<3; ++i)
    parents[i]->say();

As you can imagine, this is very powerful. It lets you extend the Parent as many times as you want and functions that take a Parent pointer will still work. For this to work as others mention you need to declare the method as virtual.

Solution 2

If the function were virtual, then you could do this and still get the output "2":

Parent* a = new Child();
a->say();

This works because a virtual function uses the actual type whereas a non-virtual function uses the declared type. Read up on polymorphism for a better discussion of why you'd want to do this.

Solution 3

Try it with:

Parent *a = new Child();
Parent *b = new Parent();

a->say();
b->say();

Without virtual, both with print '1'. Add virtual, and the child will act like a Child, even though it's being referred to via a pointer to a Parent.

Solution 4

If you do not use the virtual keyword you are not overriding, but rahter defining an unrelated method in the derived class that will hide the base class method. That is, without the virtual, Base::say and Derived::say are unrelated --besides the name coincidence.

When you use the virtual keyword (required in the base, optional in the derived class), you are telling the compiler that classes that derive from this base will be able to override the method. In that case, Base::say and Derived::say are considered overrides of the same method.

When you use a reference or pointer to a base class to call a virtual method, the compiler will add the appropriate code so that the final overrider is called (the override in the most derived class that defines the method in the hierarchy of the concrete instance in use). Note that if you do not use references/pointer but local variables, the compiler can resolve the call and it does not need to use the virtual dispatch mechanism.

Solution 5

Well I tested it for myself, because there are a lot of things we can think about:

#include <iostream>
using namespace std;
class A
{
public:
    virtual void v() { cout << "A virtual" << endl; }
    void f() { cout << "A plain" << endl; }
};

class B : public A
{
public:
    virtual void v() { cout << "B virtual" << endl; }
    void f() { cout << "B plain" << endl; }
};

class C : public B
{
public:
    virtual void v() { cout << "C virtual" << endl; }
    void f() { cout << "C plain" << endl; }
};

int main()
{
    A * a = new C;
    a->f();
    a->v();

    ((B*)a)->f();
    ((B*)a)->v();
}

output:

A plain
C virtual
B plain
C virtual

I think that a good, simple and short answer might look like this (because I think people who can understand more can memorize less thus needing for short and simple explanation):

Virtual methods checks for the DATA of the instance the pointer points to, while classic methods don't thus calling the method correponding to the specified type.

The point of that feature is the following: suppose you have an array of A's. The array can contain B's, C's, (or even derived types.). if you want to sequentially call the same method of all those instances, you would call each one you overloaded.

I find this quite tricky to understand, and obviously any C++ course should explained how this is achieved, because most of the time you are just teached about virtual functions, you use them, but until you understand how the compiler understand them and how the executable will handle the calls, you are in the dark.

The thing about VFtables is that I have never been explained what kind of code it adds, and that's obviously here where C++ requires much more experience than C, and this might be the main reason C++ was labelled as "slow" in its early days: in fact, it's powerful, but just like everything, it's powerful if you know how to use it, or else you just "blow your whole leg off".

Share:
40,896

Related videos on Youtube

anonymous
Author by

anonymous

Updated on August 25, 2020

Comments

  • anonymous
    anonymous over 3 years

    What is the purpose of using the reserved word virtual in front of functions? If I want a child class to override a parent function, I just declare the same function such as void draw(){}.

    class Parent { 
    public:
        void say() {
            std::cout << "1";
        }
    };
    
    class Child : public Parent {
    public:
        void say()
        {
            std::cout << "2";
        }
    };
    
    int main()
    {
        Child* a = new Child();
        a->say();
        return 0;
    }
    

    The output is 2.

    So again, why would the reserved word virtual be necessary in the header of say() ?

    Thanks a bunch.

  • jokoon
    jokoon over 13 years
    An explicit example would have been much appreciated I think.
  • jokoon
    jokoon over 13 years
    so except when you cast an object or when you use a derived constructor, there is no way to tell the difference between an overridden regular method and an overloaded virtual method ?
  • Donnie
    Donnie over 11 years
    You do this all the time, the classic example would be where Parent is, say, Shape, and child is a specific type of shape (like a Square). Then replace say with, for example, draw. Do you see why that would be useful? It's the exact same example as in the OP's question, just with different words.
  • user1511417
    user1511417 over 11 years
    Good example! ... But why do you do this all time? Why not Square* sq = new Square(); in the first place?
  • Donnie
    Donnie about 11 years
    You don't do it all the time, you do it when it is appropriate. What if you're creating a drawing app, and you let people pick shape brushes. You need a global (or at least object-level) variable, but don't know what kind of shape they'll pick in advance.