It may be possible that an inheritance structure may consist of a class that is derived from two or more base classes, both of which in tum are further derived from a common base class as shown below Figure. This class arrangement involves more than one form of inheritance, namely hierarchical, multiple and multilevel inheritances.
In the above Figure, the base class grandparent is inherited by two classes parent1 and parent2 which means these two classes have a separate copy of the base class members. Since the child class is derived from these two classes, it inherits the grandparent class member twice through two different paths, one through the parent1 class and the other through the parent2 class. The classes parent1 and parent2 act as a direct base class, whereas class grandparents act as an indirect base class for a class child.
When a member function of a child class accesses a grandparent class member, it results in ambiguity. It is not clear to the compiler through which of the two copies of the base class’s member it will access a grandparent class member. To understand how this ambiguity arises, let us consider the following program segment.
class grandparent { protected: int base_data; public : void base_func() { ................ //Implementation ................ } }; class parent1 public grandparent {}; class parent2 public grandparent {}; class child : public parent1,public parent2 { public: void f1() { cout<<base_data;//Error:ambiguos base_func();//Error:ambiguous } ........... };
In the above program segment, the statements
cout<<base_data; base_func();
used in the member function f1() of class child tries to access grandparent class members that result in ambiguity as the compiler cannot distinguish which one of the two copies of the base class member it will access, and the compiler generates an error.
This ambiguity can be resolved if the derived class child inherits only one copy of the indirect base class (grandparent). To accomplish this, classes (parent1 and parent2) derived directly from the common base class (grandparent) have to define this base class as a virtual base class. It is done by preceding the name of the base class (grandparent) with a keyword virtual when it is inherited by derived classes (parent1 and parent2), as shown below :
class parent1 : public virtual grandparent {}; class parent2 : virtual public grandparent {};
Note that the keyword virtual can be placed before or after the access specifier.
The process of inheriting base classes virtual so that only a single copy of its member will appear in the derived class is called virtual inheritance. The corresponding shared base class is called a virtual base class. In the above example, using virtual inheritance, the duplication of members of the base class a the ambiguities to which duplication give arise are removed.
The following program implements the above concept.
#include<iostream.h> #include<conio.h> class grandparent { protected: int base_data; public: void readgp() { cout<<"Enter data of grand parent :"; cin>>base_data; } }; class parent1 : virtual public grandparent //inherit grandparent as virtual { protected: int parent1_data; public: void readp1() { cout<<"Enter data of parent1 :"; cin>>parent1_data; } }; class parent2 : virtual public grandparent //inherit grandparent as virtual { protected: int parent2_data; public: void readp2() { cout<<"Enter data of parent2 :"; cin>>parent2_data; } }; class child : public parent1,public parent2 { private: int sum; public: void add() { sum=base_data + parent1_data+parent2_data; } void show() { cout<<"Grandparent's datamember "<<base_data; cout<<"\nData members of parent1 and parent2 = "<<parent1_data <<'\t'<<parent2_data; cout<<"\nSum of all data = "<<sum; } }; int main() { clrscr(); child c1; c1.readgp(); c1.readp1(); c1.readp2(); c1.add(); c1.show(); getch(); return 0; } Output Enter data of grandparent : 10 Enter data of parent1 20 Enter data of parent2 : 30 Grand parent's datamember : 10 Datamember of Parent1 and Parent2 Sum of all data = 60
Explanation: In the above program. The class grandparent contains a protected data member base_data and public member function readgp() to input base_data. The class parent1 and parent2 are inherited from a virtual base class (grandparent) using the keyword virtual in its class header so that only one copy of the members of a grandparent is inherited. Each of the two classes also contains a protected data member and a public member function to input their data member. The class child is derived from a parent1, and parent2 has public member functions add () for performing addition and show () for displaying all information since the grandparent class is inherited as virtual, so its data member base_data can be accessed in child class without any ambiguity.
In main, we create object cl of child class through which we input data members’ values, perform addition, and display all information. The statement
c1.readgp();
invokes readgp() of grandparent without any ambiguity as there is only one copy of this member function irrespective of the number of paths that exist between virtual base class (grandparent) and derived class (child).