Why we need virtual function?
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix; int armyCode; protected: string name; public: Army(int armycode, int suffix) : name("Shirley") { cout << "Super Class:: " << __func__ << endl; enrollment(numberSuffix, armyCode, name); } void enrollment(int numberSuffix, int armyCode, string name); string getPatientName() { cout << "Army " << __func__ << endl; return name; } }; class ArmyHospital : public Army { public: ArmyHospital() : Army(508, 13) { name = "Clara"; cout << "Derived Class: " << __func__ << endl; } string getPatientName() { cout << "ArmyHospital " << __func__ << endl; return name; } }; void Army::enrollment(int suffix, int code, string name) { armyCode = code; numberSuffix = suffix; cout << "Army::enrollment(), name is " << name << endl; } int main() { Army officer(23, 11); cout << "The officer's Name is " << officer.getPatientName() << endl << endl; ArmyHospital hospy; cout << "The patient's Name is " << hospy.getPatientName() << endl; cout << "===========================================" << endl << endl; Army* off = new Army(2,1); cout << "The 2nd officer's Name is " << off->getPatientName() << endl << endl; ArmyHospital* hos = new ArmyHospital; cout << "The 2nd patient's Name is " << hos->getPatientName() << endl << endl; cout << "===========================================" << endl << endl; return 0; }
Super Class:: Army
Army::enrollment(), name is Shirley
Army getPatientName
The officer's Name is Shirley
Super Class:: Army
Army::enrollment(), name is Shirley
Derived Class: ArmyHospital
ArmyHospital getPatientName
The patient's Name is Clara
===========================================
Super Class:: Army
Army::enrollment(), name is Shirley
Army getPatientName
The 2nd officer's Name is Shirley
Super Class:: Army
Army::enrollment(), name is Shirley
Derived Class: ArmyHospital
ArmyHospital getPatientName
The 2nd patient's Name is Clara
=========================================== |
There is no problem here, the objects officer and off could call the Army constructors respectively, and so Objects hospy, hos were able to call the ArmyHospital constructors.
It is not necessary to create an object for the parent class because whenever derived class object is created, it will obviously invoke and initialise the members of the parent class.
Can you tell what happens in the below example.
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix; int armyCode; protected: string name; public: Army(int armycode, int suffix) : name("Shirley") { cout << "Super Class:: " << __func__ << endl; enrollment(numberSuffix, armyCode, name); } void enrollment(int numberSuffix, int armyCode, string name); string getPatientName() { cout << "Army " << __func__ << endl; return name; } void getArmyclass() { cout << "Army " << __func__ << endl; return; } }; class ArmyHospital : public Army { public: ArmyHospital() : Army(508, 13) { name = "Clara"; cout << "Derived Class: " << __func__ << endl; } string getPatientName() { cout << "ArmyHospital " << __func__ << endl; return name; } void getArmyclass() { cout << "ArmyHospital" << __func__ << endl; return; } }; void Army::enrollment(int suffix, int code, string name) { armyCode = code; numberSuffix = suffix; cout << "Army::enrollment(), name is " << name << endl; } void mediator(Army *med) { cout << __func__ << endl; med->getPatientName(); } int main() { Army officer(23, 11); cout << "The officer's Name is " << officer.getPatientName() << endl << endl; ArmyHospital hospy; cout << "The patient's Name is " << hospy.getPatientName() << endl; cout << "===========================================" << endl << endl; Army* off = new Army(2,1); cout << "The 2nd officer's Name is " << off->getPatientName() << endl << endl; ArmyHospital* hos = new ArmyHospital; cout << "The 2nd patient's Name is " << hos->getPatientName() << endl << endl; cout << "===========================================" << endl << endl; mediator(off); mediator(hos); return 0; }
Super Class:: Army
Army::enrollment(), name is Shirley
Army getPatientName
The officer's Name is Shirley
Super Class:: Army
Army::enrollment(), name is Shirley
Derived Class: ArmyHospital
ArmyHospital getPatientName
The patient's Name is Clara
===========================================
Super Class:: Army
Army::enrollment(), name is Shirley
Army getPatientName
The 2nd officer's Name is Shirley
Super Class:: Army
Army::enrollment(), name is Shirley
Derived Class: ArmyHospital
ArmyHospital getPatientName
The 2nd patient's Name is Clara
===========================================
mediator
Army getPatientName // >>>>>>>>>>>>>>>>>>>>>>>
mediator
Army getPatientName // >>>>>>>>>>>>>>>>>>>>> |
Reason
I know you can't get home quickly seeing this complicated example (Laughs). But, please check only mediator function. During the compilation, the decision of which method needs to be called based on the type of the passed pointer, has been taken. This is called "Early Binding"/Static Type. In the below two lines of code, Army is the static type of med.
mediator(off); mediator(hos);
Every variable has a single static type which never changes. Early binding is also called static binding where compiler knows which machine address it has to jump when the function mediator is called. Brush up the binding concept with few minutes read here.
During the runtime, the decision of which method needs to be called based on the type of the pointed-to-object, has been taken. This is called "Late Binding"/"Dynamic Type". Dynamic type can change during the program. Dynamic type will be reflected if there is a virtual function in the base class.
In the below two lines of code, Army is the dynamic type of med whereas for the second line, ArmyHospital is the dynamic type of med. But for both the lines, the static type of med is Army.
mediator(off); // Army *med = off; mediator(hos); // Army *med = hos;
Though late-binding is little inefficient, it is widely used with the use of function pointer and virtual.
Thumb Rule: Instance of derived class can always be substituted for an instance of base class.
Though this is permitted, it might lose some information if the arguments are passed by value. This problem is called 'Slicing Problem'. This slicing problem will be avoided if pointers(Army *med) are used as in the function mediator. The below diagram depicts the slicing problem if the values are passed by value.
I again come up with another program which illustrates the same mediator functionality in more simple terms.
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix_; int armyCode_; public: Army(int armycode, int suffix) { //cout << "Parent Class:: " << __func__ << endl; } }; class ArmyHospital : public Army { private: string hospitalName_; public: ArmyHospital(int armycode, int suffix, string name) : Army(armycode, suffix) { hospitalName_ = name; //cout << "Derived Class: " << __func__ << endl; } }; int main() { Army* off = new Army(2,1); ArmyHospital* hos = new ArmyHospital(2, 110, "Rosy"); /* This is valid as instance of derived class * can always be substituted to an instance of * base class. It will not lose information */ off = hos; // This is not legal. hos = off; return 0; }
In function 'int main()':
Line 42: error: invalid conversion from 'Army*' to 'ArmyHospital*'
compilation terminated due to -Wfatal-errors. |
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f5f5f5%3BfontColor%3D%23333333%3BstrokeColor%3D%23666666%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22170%22%20y%3D%22130%22%20width%3D%22120%22%20height%3D%2260%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%223%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f5f5f5%3BfontColor%3D%23333333%3BstrokeColor%3D%23666666%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22170%22%20y%3D%22190%22%20width%3D%22120%22%20height%3D%2260%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%224%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23fff2cc%3BstrokeColor%3D%23d6b656%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22170%22%20y%3D%22250%22%20width%3D%22120%22%20height%3D%2260%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%225%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23fff2cc%3BstrokeColor%3D%23d6b656%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22370%22%20y%3D%22145%22%20width%3D%22120%22%20height%3D%2260%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%226%22%20value%3D%22%22%20style%3D%22shape%3DflexArrow%3BendArrow%3Dclassic%3Bhtml%3D1%3Brounded%3D0%3B%22%20edge%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20width%3D%2250%22%20height%3D%2250%22%20relative%3D%221%22%20as%3D%22geometry%22%3E%3CmxPoint%20x%3D%22290%22%20y%3D%22180%22%20as%3D%22sourcePoint%22%2F%3E%3CmxPoint%20x%3D%22370%22%20y%3D%22180%22%20as%3D%22targetPoint%22%2F%3E%3C%2FmxGeometry%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%227%22%20value%3D%22numberSuffix_%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22200%22%20y%3D%22145%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%228%22%20value%3D%22armyCode_%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22190%22%20y%3D%22205%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%229%22%20value%3D%22hospitalName_%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22200%22%20y%3D%22265%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2210%22%20value%3D%22hospitalName_%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22400%22%20y%3D%22160%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2211%22%20value%3D%22Army%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3D%239673a6%3BfillColor%3D%23e1d5e7%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22190%22%20y%3D%2270%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2212%22%20value%3D%22ArmyHospital%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3D%239673a6%3BfillColor%3D%23e1d5e7%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22380%22%20y%3D%2270%22%20width%3D%22100%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphMod
Solution 1
If I want ArmyHospital::getPatientName to be called, I need to create another separate function with ArmyHospital class as argument. But, this doesn't sound great. If this solution is taken, you have to write billions of functions. Okay, You can handle it using templates as well.
void mediator(ArmyHospital *med) { cout << __func__ << endl; med->getPatientName(); }
Solution 2
If we want to call the derived class function, make the base class function as virtual. That's the solution. Virtual allows the subclass method to be called when derived object is passed. In other words, virtual reacts to the dynamic type of the object it points to at run-time. Click here to understand the dynamic type with good example.
The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed.
Dispatching
- Dynamic dispatch will not happen if the virtual keyword is not specified in the base class. Click here for override.
- Dynamic dispatch will not happen if the object is not accessed through pointer or reference.
Why C++ doesn't offer virtual behavior by default?
Virtual Function
A virtual function is a member function which is declared within a base class and is re-defined(Overrided) by a derived class. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class’s version of the function.
- Virtual functions ensure that the correct function is called for an object, regardless of the type of reference (or pointer) used for function call.
- They are mainly used to achieve Runtime polymorphism
- Functions are declared with a virtual keyword in base class.
- The resolving of function call is done at Run-time.
#include <iostream> using namespace std; class Army // Class declaration { public: virtual void getPatientName() { cout << "Army " << __func__ << endl; return; } }; class ArmyHospital : public Army { public: void getPatientName() { cout << "ArmyHospital " << __func__ << endl; return; } }; void virtuality(Army *med) { cout << __func__ << endl; med->getPatientName(); } int main() { Army officer; cout << "The officer's Name is "; officer.getPatientName(); ArmyHospital hospy; cout << endl << "The patient's Name is "; hospy.getPatientName(); cout << endl << "===========================================" << endl << endl; Army* off = new Army; cout << "The 2nd officer's Name is "; off->getPatientName(); ArmyHospital* hos = new ArmyHospital; cout << endl << endl << "The 2nd patient's Name is "; hos->getPatientName(); cout << endl << "===========================================" << endl << endl; virtuality(off); virtuality(hos); return 0; }
The officer's Name is Army getPatientName
The patient's Name is ArmyHospital getPatientName
===========================================
The 2nd officer's Name is Army getPatientName
The 2nd patient's Name is ArmyHospital getPatientName
===========================================
virtuality
Army getPatientName
virtuality
ArmyHospital getPatientName |
Army* off = new Army(2,1);
mediator(off); mediator(hos);
Rules for Virtual Functions
- Virtual functions cannot be static.
- The keyword virtual should be inside the class declaration. This means the function declaration should be with virtual and can be in the header file, but the function definition can be inside the cpp file.
- A virtual function can be a friend function of another class.
- Virtual functions should be accessed using pointer or reference of base class type to achieve run time polymorphism.
- The prototype of virtual functions should be the same in the base as well as derived class.
- They are always defined in the base class and overridden in a derived class. It is not mandatory for the derived class to override (or re-define the virtual function), in that case, the base class version of the function is used.
- A class may have virtual destructor but it cannot have a virtual constructor. It is necessary to have virtual destructor, otherwise we might face consequences. Check this link here.
Array to objects
#include <iostream>
using namespace std;
class Army // Class declaration
{
private:
int numberSuffix_;
int armyCode_;
public:
Army(int armycode, int suffix) {
cout << "Parent Class:: " << __func__ << endl;
}
virtual
void warFighting() {
cout << "Army:" << __func__ << endl;
}
virtual
void treatingPatients() {
cout << "Army:" << __func__ << endl;
}
};
class ArmyHospital : public Army {
private:
string hospitalName_;
public:
ArmyHospital(int armycode, int suffix, string name) : Army(armycode, suffix) {
hospitalName_ = name;
cout << "Derived Class: " << __func__ << endl;
}
void treatingPatients() {
cout << __func__ << endl;
}
};
class ArmySchool : public Army {
private:
string schoolName_;
public:
ArmySchool(int armycode, int suffix, string name) : Army(armycode, suffix) {
schoolName_ = name;
cout << "Derived Class: " << __func__ << endl;
}
void warFighting() {
cout << __func__ << endl;
}
};
int main()
{
Army off[2];
off[0] = ArmyHospital(2, 110, "RoseHospital");
off[1] = ArmySchool(2, 110, "Vladmir Internation School");
cout << "\n" << "\n";
for (int i =0; i < 2; i++) {
off[i].warFighting();
off[i].treatingPatients();
}
return 0;
}
Array of pointers to objects
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix_; int armyCode_; public: Army(int armycode, int suffix) { cout << "Parent Class:: " << __func__ << endl; } }; class ArmyHospital : public Army { private: string hospitalName_; public: ArmyHospital(int armycode, int suffix, string name) : Army(armycode, suffix) { hospitalName_ = name; cout << "Derived Class: " << __func__ << endl; } }; class ArmySchool : public Army { private: string schoolName_; public: ArmySchool(int armycode, int suffix, string name) : Army(armycode, suffix) { schoolName_ = name; cout << "Derived Class: " << __func__ << endl; } }; int main() { Army* off[2]; off[0] = new ArmyHospital(2, 110, "RoseHospital"); off[1] = new ArmySchool(2, 110, "Vladmir Internation School"); return 0; }
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix_; int armyCode_; public: Army(int armycode, int suffix) { cout << "Parent Class:: " << __func__ << endl; } void warFighting() { cout << "Army:" << __func__ << endl; } void treatingPatients() { cout << "Army:" << __func__ << endl; } }; class ArmyHospital : public Army { private: string hospitalName_; public: ArmyHospital(int armycode, int suffix, string name) : Army(armycode, suffix) { hospitalName_ = name; cout << "Derived Class: " << __func__ << endl; } void treatingPatients() { cout << __func__ << endl; } }; class ArmySchool : public Army { private: string schoolName_; public: ArmySchool(int armycode, int suffix, string name) : Army(armycode, suffix) { schoolName_ = name; cout << "Derived Class: " << __func__ << endl; } void warFighting() { cout << __func__ << endl; } }; int main() { Army* off[2]; off[0] = new ArmyHospital(2, 110, "RoseHospital"); off[1] = new ArmySchool(2, 110, "Vladmir Internation School"); cout << "\n" << "\n"; for (int i =0; i < 2; i++) { off[i]->warFighting(); off[i]->treatingPatients(); } return 0; }
off[i]->warFighting(); off[i]->treatingPatients();
#include <iostream> using namespace std; class Army // Class declaration { private: int numberSuffix_; int armyCode_; public: Army(int armycode, int suffix) { cout << "Parent Class:: " << __func__ << endl; } virtual void warFighting() { cout << "Army:" << __func__ << endl; } virtual void treatingPatients() { cout << "Army:" << __func__ << endl; } }; class ArmyHospital : public Army { private: string hospitalName_; public: ArmyHospital(int armycode, int suffix, string name) : Army(armycode, suffix) { hospitalName_ = name; cout << "Derived Class: " << __func__ << endl; } void treatingPatients() { cout << __func__ << endl; } }; class ArmySchool : public Army { private: string schoolName_; public: ArmySchool(int armycode, int suffix, string name) : Army(armycode, suffix) { schoolName_ = name; cout << "Derived Class: " << __func__ << endl; } void warFighting() { cout << __func__ << endl; } }; int main() { Army* off[2]; off[0] = new ArmyHospital(2, 110, "RoseHospital"); off[1] = new ArmySchool(2, 110, "Vladmir Internation School"); cout << "\n" << "\n"; for (int i =0; i < 2; i++) { off[i]->warFighting(); off[i]->treatingPatients(); } return 0; }
Army *army1[2]; army1[0] = new ArmyHospital(); army1[1] = new ArmySchool(); Army *army1 = new ArmyHospital[2]();
Another reason: Why we need virtual?
#include <iostream> class Car { public: Car (std::string color) : color_(color) {} std::string getColor() const { return color_; } std::string brand() const { std::string res; //Tesla - color: Black res += type_; res += "color: " + getColor(); return res; } protected: std::string color_; std::string type_; }; class Tesla : public Car { public: Tesla (std::string color) : Car(color) { type_ = "Tesla"; } }; class Benz : public Car { public: Benz (std::string color) : Car(color) { type_ = "Benz"; } }; int main() { Tesla t1("Black"); std::cout << t1.brand() << std::endl; return 0; }
#include <iostream> class Car { public: Car(std::string color) : color_(color) {} std::string getColor() const { return color_; } std::string brand() const { std::string res; res += "name: "; res.append(getName()); res += ", color: " + color_; return res; } virtual std::string_view getName() const = 0; protected: std::string color_; }; class Tesla : public Car { public: Tesla(std::string color) : Car(color) {} virtual std::string_view getName() const override { return "Tesla"; } }; class Benz : public Car { public: Benz(std::string color) : Car(color) {} virtual std::string_view getName() const override { return "Benz"; } }; int main() { Tesla t1("Black"); std::cout << t1.brand() << std::endl; return 0; }
#include <iostream> using namespace std; int main() { string name; //It's important to give the prompt to user for the input cout << "Enter the user's name "; getline(cin, name); cout << "The entered user's name is " << name; }
Output
References
https://courses.cs.washington.edu/courses/cse143/00wi/lectures/r-dynamic-dispatch.pdf
Comments
Post a Comment