Skip to content

Files

Latest commit

 

History

History
 
 

Virtual Functions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Virtual Functions

C++ allows us to use pointers, but in doing so, we lose the ability to use overriding functions in classes. Luckily, we can work around this issue by designating base class functions with the keyword virtual. This indicates to the compiler that the base class function may be overridden by a function (of the same name) in the derived class.

Example: function override fails with pointer object

Below is an example of defining a pointer to the derived class (using the base class), where the pointer fails to perform a function override.

#include <iostream>
using namespace std;

class Mammal {
  private:
    string printStatement = "Mammals can give direct birth.";

  public: 
    void printMe(){
      cout << "Mammal:: " << printStatement << "\n";
    }

};

class Bat: public Mammal {

  private:
    string printStatement = "A new animal.";
  
  public:
    void printMe(){
      cout << "Bat:: " << printStatement << "\n";
    }

};

int main() {
  Bat b1;
  
  // function overriding
  b1.printMe();

  // failed funciton overriding with pointer
  Mammal* prtB = &b1;
  prtB->printMe();
  
  return 0;
}
// Bat:: A new animal.
// Mammal:: Mammals can give direct birth.

From the above example, we would expect that a pointer to b1 (Mammal* prtB = &b1;) would print the same statement as b1 itself (i.e. from the derived class) due to function overloading, however this is not the case, and we find that the ptrB prints the statement defined in the Mammal class (i.e. the base class).

Example: virtual keyword

The pointer in the above example failed to perform a function override with the printMe() call. To correct this, we can add the keyword virtual the function of the base class that we intend to override.

#include <iostream>
using namespace std;

class Mammal {
  private:
    string printStatement = "Mammals can give direct birth.";

  public: 
    // declaring the function for override
    virtual void printMe(){
      cout << "Mammal:: " << printStatement << "\n";
    }

};

class Bat: public Mammal {

  private:
    string printStatement = "A new animal.";
  
  public:
    void printMe(){
      cout << "Bat:: " << printStatement << "\n";
    }

};

int main() {
  Bat b1;
  
  // function overriding
  b1.printMe();

  // success funciton overriding with pointer
  Mammal* prtB = &b1;
  prtB->printMe();
  
  return 0;
}
// Bat:: A new animal.
// Bat:: A new animal.

In the above example, the function defined in the base class was indeed overridden. This indicates that the address the pointer prtB points to is that of the derived Bat class, and not the original Mammal class.

Subtleties of virtual functions

There are (atleast) two cases where virtual functions will need to be defined: (1) if you are passing objects by reference to a function (e.g. printVal()), the base version of the method will be executed (we pass pointers because copying objects may take up too much memory), (2) if we loop through a vector of objects, the type has to be consistent, objects may be different derived classes, so the base class may be used to define the vector type. In the example below, not using virtual results in print outs of the base class.

#include <iostream>
#include <vector>using namespace std;
// -- Solution add 'virtual' keyword
class Mammal { 
  private: string printStatement = "Mammals can give direct birth."; 
  public: void printMe(){ cout << "Mammal:: " << printStatement << "\n"; }
  };

class WingedAnimal: public Mammal { 
  private: string printStatement = "An animal with wings."; 
  public: void printMe(){ cout << "WingedAnimal:: " << printStatement << "\n"; }
  };
  
class Bat: public Mammal {
  private: string printStatement = "A new animal."; 
  public: void printMe(){ cout << "Bat:: " << printStatement << "\n"; }
  };
  
void printVal(Mammal *ptr){ptr->printMe();}

int main() {
 // Example 1: 
 // Calling a function to execute an object method printVal() 
 // When passing a reference only the base class is printed. 
 Bat* b1 = new Bat; 
 b1->printMe(); 
 printVal(b1); // prints base version of function
 
 Mammal* ptrB = new Mammal; 
 ptrB->printMe(); 
 printVal(ptrB);
 
 Mammal* ptrC = new WingedAnimal; 
 ptrC->printMe(); 
 printVal(ptrC);

 // Example 2: 
 // Iterating through list of objects 
 // It is not feasible to manually resolve scope 
 cout << "\nPrinting from list:" << endl;
 // vector elements have to be defined from the same class 
 vector<Mammal *> listOfMammals = {ptrB, ptrC}; 
 for (int i=0; i<listOfMammals.size(); i++){ listOfMammals[i]->printMe(); }
}
// Bat:: A new animal.
// Mammal:: Mammals can give direct birth.
// Mammal:: Mammals can give direct birth.
// Mammal:: Mammals can give direct birth.
// Mammal:: Mammals can give direct birth.
// Mammal:: Mammals can give direct birth.
// Printing from list:
// Mammal:: Mammals can give direct birth.
// Mammal:: Mammals can give direct birth.

References: