Understanding Virtual Functions with Inherited Classes

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Environment:

You probably know what a class constructor in C++ is. Then you have used the functionality of virtual functions. Why? Class constructors realize many things that are out of sight for the programmer, such as initializing the class methods, attributes, base virtual pointers (vbptr), and virtual function pointers (vfptr), making inline methods and some other things you know are made, but many times don’t realize how?

Class constructors, when executed, allocate in memory an object of a defined type. This object can inherit properties from a base class, and if this is the case, class constructors use virtual function pointers to point to the class’s virtual function table (v-table). That is a table that points to the members of the interfaces that an object supports and enables proper binding of virtual functions.

If a constructor is overloaded, this constructor can become polymorphic; in other words, it will perform different operations depending on the type of arguments it receives when it is bound at run time.

For example, you could have a generic class constructor Person(){} and an overloaded class constructor Person(string personName, float personAge){}. The first constructor could initialize the object in memory with default values, but the object that is instantiated with the constructor that receives as an argument age will initialize an object with a defined age. If our class person inherits properties from a base class LivingThing(){}, in this case we will have polymorphic class constructors that are derived from a base class LivingThing(){}.

The output would have been this:

The constructor bound the proper method according to the arguments received, or when the object is instantiated. Polymorphism is achieved through late binding or dynamic binding. Dynamic binding is when at run time it is determined which function to call for a particular object or determining what type of method will be used until a program is on its execution phase. Polymorphism allows programmers a great deal of flexibility and power.

The opposite of dynamic binding is static binding; that is when at compile time it is determined which functions will call a particular object. This approach of static binding does not allow exploiting the full potential of object-oriented programming.

You might be wondering when to use virtual functions, and whether this causes a lot of security and control problems. Remember that a derived class inherits the members of its base class but only those that are declared public or protected. If you need an extra control, you could use static methods and attributes to control the number of objects derived from a given class.

Base classes have the common code implemented on them, and the specific code is implemented on the derived classes. It is recommended that the base class establish a protocol through which the derived classes can attain maximum capability. Using virtual functions allows you to do this, and if you are not convinced with the methods on the base class, you can always override a method.

You can use the following code and compile it to test the properties of virtual functions. The general description of the classes is:

Base Class: Human

Derived Classes: sportsPro and Trainer


Human Class Description

SportsPro Class Description

Trainer Class Description


//++++++++++++++++++++++++ Beginning of the Code +++++++++++++++++
// Gustavo Paris Arce
// Virtual Functions
// Language C++
// Email gusabc_mx@yahoo.com.mx <mailto:gusabc_mx@yahoo.com.mx>

#include <ctype.h>
#include <string.h>
#include <iostream.h>

class Human
             // Base Class, inherited classes uses its attributes
             // and methods
{
private:
  char
  name[100]; // This is the name attribute from the base class

protected:
  float
  numberTrainingHours; // Float data type attribute to describe
                       // the number of hours of training
  float
  HomePay;             // Float data type that describes the

public:
  Human(char*
        myname, float
        mynumberTrainingHours);
  const
  char*
    getName()
{
  return
    name;
};

//Virtual functions will be implemented in the derived classes
{
virtual
void
  calcHomePay() = 0;
virtual
void
  print() = 0;
};

    class sportsPro :
    public Human
    // Class sportsPro inherits properties from base class Human
{
private:
static
int
  payPerPub;

static
float
  payPerPubPromoted;

char
  Promoted;   // char attribute that determines whether the
              // sportsPro is promoted or has a sponsorship

protected:

int
  Tournaments;

public:
  sportsPro(char*
  myname, float
  mynumberTrainingHours,
  char amIPromoted,
  int pubs);

void
  calcHomePay();    // This method is declared and implemented
                    // by the same class

void
  print();          //Overrides method from base class and prints
                    // the output in the screen
  };

  class Trainer :
  public Human

{

private:
static
int
  payPerSem;

protected:
int
  semExperience;

public:
  Trainer(char*
          myname, float
          mynumberTrainingHours,
          int sems);

void
  calcHomePay();

void
  print();
  };

  Human::Human(char
  *myname, float
  mynumberTrainingHours)
  {
  strcpy(this->name, myname);

this->numberTrainingHours = mynumberTrainingHours;
};

  Trainer::Trainer(char
  *myname, float
  mynumberTrainingHours,
  int sems):Human(myname, mynumberTrainingHours)

{
this->semExperience = sems;
};

  void Human::print()
  // This method was declared virtual, and now it is implemented
  // for class human
  {
  cout<< "Home Pay: " <<
  this->numberTrainingHours <<
  endl;
  }

  void Trainer::calcHomePay()
  {
float
  toTrainerlPay;
  toTrainerlPay = this->numberTrainingHours
                + ((this->semExperience)*50);

this->numberTrainingHours = toTrainerlPay;
  }

  void sportsPro::calcHomePay()
// This method was declared virtual in the base class Human
// and now is implemented in the derived class sportsPro
  {
float
  toTrainerlPay;

if(this->Promoted == 'Y')
  {
  toTrainerlPay = this->numberTrainingHours
                + ((this->Tournaments)*200);
  }

else
  toTrainerlPay = this->numberTrainingHours
                + ((this->Tournaments)*100);

                  this->numberTrainingHours
                = toTrainerlPay;
  }

  void sportsPro::print()
  // Method declared in base class as virtual, overridden in
  // sportsPro class and now implemented

  { const
char
  *tmp;
  tmp = Human::getName();

char
  nameX[100];

for(int
  fill = 0; fill< 100; fill++)
  {
  nameX[fill] = '@';
  }

  strcpy(nameX, tmp);
  cout<< "Professional Name: ";

for(int
  i = 0; i < 100; i++)
  {

if(nameX[i] != '@')
  {
  cout << nameX[i];
   
  }
  }

if(this->Promoted=='Y')
  cout<< "" <<endl;

else
  cout << "without promotion"<<endl;
  cout << ": " <<this->Tournaments<<endl;
  Human::print();
  }

  void Trainer::print()
  {
const
char
  *tmp;
  tmp = Human::getName();

char
  nameX[100];

for(int
  fill = 0; fill< 100; fill++)
  {
  nameX[fill] = '@';
  }

  strcpy(nameX, tmp);
  cout<< "Name: ";

for(int
  i = 0; i < 100; i++)
  {

if(nameX[i] != '@')
  {
  cout << nameX[i];

  }
  }
  cout <<endl;
  Human::print();

  }
  sportsPro::sportsPro(char
                       *myname, float
                       mynumberTrainingHours,
                       char amIPromoted,
                       int pubs) : Human(myname,
                                         mynumberTrainingHours)
  {

this->Tournaments = pubs;
this->Promoted= amIPromoted;
  };

  #define MAXPEOPLE 4
  int main()
  {
  cout << "************ Virtual Functions**************";
  cout << "Gustavo R. Pares";
  cout << "gusabc_mx@yahoo.com.mx";
  cout << "http://mx.geocities.com/gusabc_mx ";
  cout << "********************************************";
  Human* arr[MAXPEOPLE];

int
  i;

  arr[0] = new
  sportsPro("Andre A.", 4112.50, 'N', 3);

  arr[1] = new
  sportsPro("Joe M.", 4112.50, 'Y', 10);
  arr[2] = new
  Trainer("Mr. Wilson ", 1000.50, 1);
  arr[3] = new
  Trainer("Mr. Adidas", 1000.50, 6);

for(i=0 ; i< MAXPEOPLE ; ++i)
  {
  arr[i]->calcHomePay();
  arr[i]->print();
  cout<<endl;
  }
for(i=0 ; i<MAXPEOPLE ; ++i)
delete
  arr[i];
int
  out = 1;
  cout<< "- To exit type (4) >> ";
do{
cin >> out;
  }while(out < 2);    // When user types any number greater
                      // than 2, the cycle finishes
return
  0;
  }

//+++++++++++++++++++++++++ End of Code +++++++++++++++++++++++

Bibliography

Questions and Answers

  • What does the code do?
    Just prints some lines showing the different output depending on the class whether it was a base class or not and the arguments it receives.
  • How do I integrate it with my existing code or how do I use it?
    Well, you have to have a good insight of the class structure of your program. Then you have to declare virtual all functions that you consider will be overridden in other classes. After that, you have to implement the virtual functions in each class you pretend to use it with the proper arguments for that class. Finally, you have to think carefully whether you want your derived classes to be able to implement the methods as they want. Project managers should pay attention to this point; otherwise, the scope of a method may be lost when implementing in derived classes.
  • If there is a similar article on CodeGuru already, how does this one differ? Why would someone want to use your version?
    On CodeGuru there are some articles related to virtual functions, but none of them is concerned about explaining, only how virtual functions work with inherited classes.
  • What version of software was this code built with?
    It was compiled and built with Visual Studio C++.NET

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read