Solved

Virtual functions with examples

Posted on 2003-12-08
6
570 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
Complete VMware vSphere® ESX(i) & Hyper-V Backup

Capture your entire system, including the host, with patented disk imaging integrated with VMware VADP / Microsoft VSS and RCT. RTOs is as low as 15 seconds with Acronis Active Restore™. You can enjoy unlimited P2V/V2V migrations from any source (even from a different hypervisor)

 

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

Enterprise Mobility and BYOD For Dummies

Like “For Dummies” books, you can read this in whatever order you choose and learn about mobility and BYOD; and how to put a competitive mobile infrastructure in place. Developed for SMBs and large enterprises alike, you will find helpful use cases, planning, and implementation.

Question has a verified solution.

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

The article will include the best Data Recovery Tools along with their Features, Capabilities, and their Download Links. Hope you’ll enjoy it and will choose the one as required by you.
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

830 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