Overriding vs Virtual

2019-01-13 05:46发布

问题:

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.

回答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[3] parents;
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 let's 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.



回答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.



回答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.



回答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.



回答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".



回答6:

When you use the keyword virtual, a virtual function table is created to locate the correct methods in an instance. Then, even if the derived instance is pointed to by a base class pointer, it will still find the correct implementation of the method.



回答7:

This is a very important aspect of c++ programming-- almost every interview I've been to, I get asked this question.

What happens if you change your main to:

int main() { Parent* a = new Child(); a->say(); return 0; }

Also, it's worth understanding what a vtable is.