Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need virtual functions in C++?

People also ask

Why do we need virtual function when do we make a virtual function pure?

A pure virtual function is a function that must be overridden in a derived class and need not be defined. A virtual function is declared to be “pure” using the curious =0 syntax. For example: class Base {

What happens if we don't use a virtual function in the inheritance?

If you don't use virtual functions, you don't understand OOP yet. Because the virtual function is intimately bound with the concept of type, and type is at the core of object-oriented programming, there is no analog to the virtual function in a traditional procedural language.

Why do we use virtual functions in C++?

A virtual function in C++ helps ensure you call the correct function via a reference or pointer. The C++ programming language allows you only to use a single pointer to refer to all the derived class objects.


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 won't 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() from the Animal class a virtual function:

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

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

Main:

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

Done.


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* basePtr = new Derived ();
  //  Note - constructed as Derived, but pointer stored as Base*

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

EDIT - see this question.

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


You need at least 1 level of inheritance and an upcast to demonstrate it. Here is a very simple example:

class Animal
{        
    public: 
      // turn the following virtual modifier on/off to see what happens
      //virtual   
      std::string Says() { return "?"; }  
};

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

    std::cout << d->Says();   // always Woof
    std::cout << a->Says();   // Woof or ?, depends on virtual
}

Virtual Functions are used to support Runtime Polymorphism.

That is, virtual keyword tells the compiler not to make the decision (of function binding) at compile time, rather postpone it for runtime".

  • You can make a function virtual by preceding the keyword virtual in its base class declaration. For example,

     class Base
     {
        virtual void func();
     }
    
  • When a Base Class has a virtual member function, any class that inherits from the Base Class can redefine the function with exactly the same prototype i.e. only functionality can be redefined, not the interface of the function.

     class Derive : public Base
     {
        void func();
     }
    
  • A Base class pointer can be used to point to Base class object as well as a Derived class object.

  • When the virtual function is called by using a Base class pointer, the compiler decides at run-time which version of the function - i.e. the Base class version or the overridden Derived class version - is to be called. This is called Runtime Polymorphism.

You need virtual methods for safe downcasting, simplicity, and conciseness.

That’s what virtual methods do: they downcast safely, with apparently simple and concise code, avoiding the unsafe manual casts in the more complex and verbose code that you otherwise would have.


Non-virtual method ⇒ static binding ========================================

The following code is intentionally “incorrect”. It doesn’t declare the value method as virtual, and therefore produces an unintended “wrong” result, namely 0:

#include <iostream>
using namespace std;

class Expression
{
public:
    auto value() const
        -> double
    { return 0.0; }         // This should never be invoked, really.
};

class Number
    : public Expression
{
private:
    double  number_;
    
public:
    auto value() const
        -> double
    { return number_; }     // This is OK.

    Number( double const number )
        : Expression()
        , number_( number )
    {}
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;
    
public:
    auto value() const
        -> double
    { return a_->value() + b_->value(); }       // Uhm, bad! Very bad!

    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    {}
};

auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );
    
    cout << sum.value() << endl;
}

In the line commented as “bad” the Expression::value method is called, because the statically known type (the type known at compile time) is Expression, and the value method is not virtual.


Virtual method ⇒ dynamic binding. ======================================

Declaring value as virtual in the statically known type Expression ensures that each call will check what actual type of object this is, and call the relevant implementation of value for that dynamic type:

#include <iostream>
using namespace std;

class Expression
{
public:
    virtual
    auto value() const -> double
        = 0;
};

class Number
    : public Expression
{
private:
    double  number_;
    
public:
    auto value() const -> double
        override
    { return number_; }

    Number( double const number )
        : Expression()
        , number_( number )
    {}
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;
    
public:
    auto value() const -> double
        override
    { return a_->value() + b_->value(); }    // Dynamic binding, OK!

    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    {}
};

auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );
    
    cout << sum.value() << endl;
}

Here the output is 6.86 as it should be since the virtual method is called virtually. This is also called dynamic binding of the calls. A little check is performed, finding the actual dynamic type of object, and the relevant method implementation for that dynamic type is called.

The relevant implementation is the one in the most specific (most derived) class.

Note that method implementations in derived classes here are not marked virtual, but are instead marked override. They could be marked virtual but they’re automatically virtual. The override keyword ensures that if there is not such a virtual method in some base class, then you’ll get an error (which is desirable).


The ugliness of doing this without virtual methods ==================================================

Without virtual one would have to implement some Do It Yourself version of the dynamic binding. It’s this that generally involves unsafe manual downcasting, complexity, and verbosity.

For the case of a single function, as here, it suffices to store a function pointer in the object and call via that function pointer, but even so it involves some unsafe downcasts, complexity, and verbosity, to wit:

#include <iostream>
using namespace std;

class Expression
{
protected:
    typedef auto Value_func( Expression const* ) -> double;

    Value_func* value_func_;

public:
    auto value() const
        -> double
    { return value_func_( this ); }
    
    Expression(): value_func_( nullptr ) {}     // Like a pure virtual.
};

class Number
    : public Expression
{
private:
    double  number_;
    
    static
    auto specific_value_func( Expression const* expr )
        -> double
    { return static_cast<Number const*>( expr )->number_; }

public:
    Number( double const number )
        : Expression()
        , number_( number )
    { value_func_ = &Number::specific_value_func; }
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;
    
    static
    auto specific_value_func( Expression const* expr )
        -> double
    {
        auto const p_self  = static_cast<Sum const*>( expr );
        return p_self->a_->value() + p_self->b_->value();
    }

public:
    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    { value_func_ = &Sum::specific_value_func; }
};


auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );
    
    cout << sum.value() << endl;
}

One positive way of looking at this is, if you encounter unsafe downcasting, complexity, and verbosity as above, then often a virtual method or methods can really help.


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 allows runtime polymorphism, the core concept of OO programming, while the latter does not.


I would like to add another use of Virtual function though it uses the same concept as above stated answers but I guess its worth mentioning.

VIRTUAL DESTRUCTOR

Consider this program below, without declaring Base class destructor as virtual; memory for Cat may not be cleaned up.

class Animal {
    public:
    ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat() {
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

Output:

Deleting an Animal
class Animal {
    public:
    virtual ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat(){
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

Output:

Deleting an Animal name Cat
Deleting an Animal