views:

190

answers:

5

I'm learning C++ and I'm just getting into Virtual Functions/Methods.

From what I've read (in the book and online), Virtual Methods are methods in the a base class that you can override in derived classes.

But earlier in the book, when learning about basic inheritance, I was able to override base methods in derived classes without using 'virtual'.

So what am I missing here? I know there is more to virtual methods and it seems to be important so I want to be clear on what it is exactly. I just can't find a straight answer online.

+8  A: 

You need at least 1 level of inheritance and a downcast to demonstrate it. Here is a very simple (untested) sample:

class Animal
{
    // virtual
    public: std::string Says() { return "?"; }  // turn virtual on/off
};

class Dog: public Animal
{
    public: std::string Says() { return "Woof"; }
};


void test()
{
    Dog* d = new Dog();
    Animal* a = d;    // refer to Dog instance with Animal pointer

    cout << d->Says();   // always Woof
    cout << a->Says();   // Woof or ?, depends on virtual
}
Henk Holterman
@Prasoon, thanks for the Edit, I've been doing mostly C# lately.
Henk Holterman
Your example says that the returned string depends on whether the function is virtual, but it doesn't say which result corresponds to virtual and which corresponds to non-virtual. Additionally, it's a little confusing as you're not using the string that's being returned.
Ross
@Ross: that leaves something for the reader to explore. But I'll edit a little.
Henk Holterman
A: 

You have to distinguish between overriding and overloading. Without the virtual keyword you only overload a method of a base class. This means nothing but hiding. Let's say you have a base class Base and a derived class Specialized which both implement void foo(). Now you have a pointer to Base pointing to an instance of Specialized. When you call foo() on it you can observe the difference that virtual makes: If the method is virtual, the implementation of Specialized will be used, if it is missing, the version from Base will be chosen. It is best practice to never overload methods from a base class. Making a method non-virtual is the way of its author to tell you that its extension in subclasses is not intended.

h0b0
+1  A: 

If the base class is Base, and a derived class is Der, you can have a Base *p pointer which actually points to an instance of Der. When you call p->foo();, if foo is not virtual, then Base's version of it executes, ignoring the fact that p actually points to a Der. If foo is virtual, p->foo() executes the "leafmost" override of foo, fully taking into account the actual class of the pointed-to item. So the difference between virtual and non-virtual is actually pretty crucial: the former allow runtime polymorphism, the core concept of OO programming, while the latter don't.

Alex Martelli
I hate to contradict you, but compile-time polymorphism is still polymorphism. Even overloading non-member functions is a form of polymorphism - ad-hoc polymorphism using the terminology in your link. The difference here is between early and late binding.
Steve314
@Steve314, you're pedantically correct (as a fellow pedant, I approve that;-) -- editing the answer to add the missing adjective;-).
Alex Martelli
Pedantically correct is the only *real* correct. Anything else is just wrong and stupid. I've tried to explain to people how wrong and stupid they are. I could certainly help a lot of people by explaining precisely how wrong and stupid they are and how to be more superior, like me, but for some reason they won't stop hitting me! How stupid is that!
Steve314
+2  A: 

Without "virtual" you get "early binding". Which implementation of the method is used gets decided at compile time based on the type of the pointer that you call through.

With "virtual" you get "late binding". Which implementation of the method is used gets decided at run time based on the type of the pointed-to object - what it was originally constructed as. This is not necessarily what you'd think based on the type of the pointer that points to that object.

class Base
{
  public:
            void Method1 ()  {  std::cout << "Base::Method1" << std::endl;  }
    virtual void Method2 ()  {  std::cout << "Base::Method2" << std::endl;  }
};

class Derived : public Base
{
  public:
    void Method1 ()  {  std::cout << "Derived::Method1" << std::endl;  }
    void Method2 ()  {  std::cout << "Derived::Method2" << std::endl;  }
};

Base* obj = new Derived ();
  //  Note - constructed as Derived, but pointer stored as Base*

obj->Method1 ();  //  Prints "Base::Method1"
obj->Method2 ();  //  Prints "Derived::Method2"

EDIT - see this question.

Also - this tutorial covers early and late binding in C++.

Steve314
A: 

I'm a C++ newbie myself, but here is how I understood not just what virtual functions are, but why they're required:

Let's say you have these two classes:

class Animal
{
public:
void eat() { std::cout << "I'm eating generic food."; }
}

class Cat : public Animal
{
public:
void eat() { std::cout << "I'm eating a rat."; }
}

In your main function:

Animal *animal = new Animal;
Cat *cat = new Cat;

animal->eat(); // outputs: "I'm eating generic food."
cat->eat();    // outputs: "I'm eating a rat."

So far so good right? Animals eat generic food, cats eat rats, all without virtual.

Let's change it a little now so that eat() is called via an intermediate function (a trivial function just for this example):

//this can go at the top of the main.cpp file
void func(Animal *xyz) { xyz->eat(); }

Now our main function is:

Animal *animal = new Animal;
Cat *cat = new Cat;

func(animal) // outputs: "I'm eating generic food."
func(cat)    // outputs: "I'm eating generic food."

Uh oh...we passed a Cat into func(), but it wont eat rats. Should you overload func() so it takes a Cat* ? If you have to derive more animals from Animal they would all need their own func().

The solution is to make eat() a virtual function:

class Animal
{
public:
virtual void eat() { std::cout << "I'm eating generic food."; }
}

Main:

func(animal) // outputs: "I'm eating generic food."
func(cat)    // outputs: "I'm eating a rat."

Done.

M Perry