Solved

Virtual functions with examples

Posted on 2003-12-08
6
569 Views
Last Modified: 2013-11-15
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

0
Comment
Question by:anushan
  • 3
  • 2
6 Comments
 
LVL 16

Expert Comment

by:imladris
ID: 9900232
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 9900302
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 9900309
imladris, sorry about the post. Your post wasn't there when I looked and started typing.
0
Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

 

Author Comment

by:anushan
ID: 9900626
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 9900803
It does if you're trying to implement functionality equal to MR and MC on a calculator.
0
 
LVL 16

Accepted Solution

by:
imladris earned 200 total points
ID: 9904951
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

Featured Post

Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Replication has always been one of those technologies that people run scared from. The main reason is usually cost. When you think of replication, your mind drifts to solutions that replicate from one disk frame to another using block level technolo…
VM backups can be lost due to a number of reasons: accidental backup deletion, backup file corruption, disk failure, lost or stolen hardware, malicious attack, or due to some other undesired and unpredicted event. Thus, having more than one copy of …
To efficiently enable the rotation of USB drives for backups, storage pools need to be created. This way no matter which USB drive is installed, the backups will successfully write without any administrative intervention. Multiple USB devices need t…
This tutorial will walk an individual through the process of configuring basic necessities in order to use the 2010 version of Data Protection Manager. These include storage, agents, and protection jobs. Launch Data Protection Manager from the deskt…

786 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question