Solved

Virtual functions with examples

Posted on 2003-12-08
6
572 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

U.S. Department of Agriculture and Acronis Access

With the new era of mobile computing, smartphones and tablets, wireless communications and cloud services, the USDA sought to take advantage of a mobilized workforce and the blurring lines between personal and corporate computing resources.

Question has a verified solution.

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

A Bare Metal Image backup allows for the restore of an entire system to a similar or dissimilar hardware. They are highly useful for migrations and disaster recovery. Bare Metal Image backups support Full and Incremental backups. Differential backup…
Microservice architecture adoption brings many advantages, but can add intricacy. Selecting the right orchestration tool is most important for business specific needs.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

728 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