Virtual functions with examples

Hi experts,

Can someone explain to me clearly what Virtual functions are???
Suppose I have a simple class for a four function calculator as follows:

class calculator {
public:
virtual int add (int a, int b) { return a + b; }
virtual int sub (int a, int b) { return a - b; }
virtual int mul (int a, int b) { return a * b; }
virtual int div (int a, int b) { return a / b; }
};

Now I want to  derive a new class that adds memory to the class so that the result
of each calculation will be stored in that memory .

I also want the following functions to be part of the derived class:

int get_mem() and int clear_mem()

I would like to understand the concept of virtual functions..can someone guide me !

thanks

anushanAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

imladrisCommented:
In straight inheritance a virtual function will not provide any benefits. The benefits of virtual functions arrive when you are dealing with some number of objects, all of which have been derived from some common ancestor, and you want to treat them similarly.

The classic example is for a Shape class. Suppose one of the methods Shape provides is area(), and that there are two derived classes Square and Circle. The area function for Square is "return(width*height);". The area function for Circle is "return(pi*radius*radius);".

Now there is some other class, and all it knows is that it is dealing with an array of Shape classes.

Shape shparray[10];

Now how can it calculate the total area of all the shapes it has? If it calls area, which method will be called? A method defined in Shape? Or a method defined in the actual class? How will it know what code to jump to?

The correct answer to these questions lies in the use of virtual functions. A virtual function is "late-binding". That means that the class object itself has information about itself sufficient to resolve these questions. So when the areacalculating class calls:

total=shparray[0].area();

The executable is able to determine which area method to call. This kind of behaviour is also referred to as polymorphism. A situation in which a method call on an object will behave "appropriately" depending on the type of the object.

Note that for this to work, Shape must provide an area method. In this case it would probably be declared "pure virtual", that is, without any definition. But without that you would not be able to call the area method for Shape objects.

So, in the above example, where you are deriving a new calculator class with additional methods, there is no apparent way for this to be useful in and of itself. Unless there is some other class that wants to treat your new calculator as a regular calculator, and you can amend the regular calculator class to provide at least an interface to the new functionality, there will be no benefit.
0
Tommy BraasCommented:
To quote "C++ Primer, Third Edition" by Lippman et al.:
"When a member function is virtual, the function invoked is the one defined in the dynamic type of the class object (or pointer or reference) through which it is invoked."

A bit convoluted...What it means is that which version of a virtual member function depends on the dynamic type of the pointer or reference of the object on which you're trying to invoke the virtual member function....hmmm...that was convoluted too...Let's try an example!

class LoanType1
{
   public:
      virtual float calculateMonthlyAmount(float principal){ return principal * 0.1; }
}

class LoanType2 : LoanType1
{
      virtual float calculateMonthlyAmount(float principal){ return principal * 0.2/4; }
}

Say now that we have a reference to a LoanType2:
LoanType2 *myLoan2 = ...;
LoanType1 *myLoan1 = dynamic_cast<LoanType1*>myLoan2;
float amount2 = myLoan2->calculateMonthlyAmount(12);         // this amount will be calculated using the function declared in LoanType2!
float amount1 = myLoan1->calculateMonthlyAmount(12);         // this amount will be calculated using the function declared in LoanType1, even though the "real" type of the class is LoanType2!

So, amount2 != amount1 (unless the value of principal is 0) !

I hope this clarifies things!
0
Tommy BraasCommented:
imladris, sorry about the post. Your post wasn't there when I looked and started typing.
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

anushanAuthor Commented:
thanks for explaining that to me.

Now I am sure that the example I posted doesn't utilize the concept of virtual functions well...but here's what I tried with it (just straight inheritence)......does this make sense??

#include <iostream>

using namespace std;

class calculator {
public:
virtual int add (int a, int b) { return a + b; }
virtual int sub (int a, int b) { return a - b; }
virtual int mul (int a, int b) { return a * b; }
virtual int div (int a, int b) { return a / b; }
};

class derived1 : public calculator {
private:
   int element;
public:
   int add (int a, int b) { return (element = a + b); }
   int sub (int a, int b) { return (element = a - b); }
   int mul (int a, int b) { return (element = a * b); }
   int div (int a, int b) { return (element = a / b); }
   int get_mem() { return element; }
   int clear_mem() { return (element = 0); }
};


calculator *bptr = new derived1;

int main()
{
  bptr->add(2, 3);
  cout << "Element is: " << ((derived1 *)bptr)->get_mem() << endl;
  bptr->mul(4, 5);
  ((derived1 *)bptr)->get_mem();
  ((derived1 *)bptr)-> clear_mem();
  return 0;
}
0
Tommy BraasCommented:
It does if you're trying to implement functionality equal to MR and MC on a calculator.
0
imladrisCommented:
Yup, that's fine code.

In practical terms for that code as it stands, there is, of course, no real need to declare bptr to be calculator. All the casts could be removed, and the type checking would be stronger if you declared bptr to be what it is: derived1. Then you would get:

derived1 *bptr = new derived1;

int main()
{
  bptr->add(2, 3);
  cout << "Element is: " << bptr->get_mem() << endl;
  bptr->mul(4, 5);
  bptr->get_mem();
  bptr-> clear_mem();
  return 0;
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Storage Software

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.