This post is a continuation to Part 1 of this article, hence for a better understanding, kindly skim through the first part if you have not done so already, before you begin with Part 2.
As mentioned in the earlier part, in this post, we get more insight into object layout, implicit pointer conversions, pointer offsetting and virtual base class pointers in the case of multiple inheritance. And in the process, we get a good understanding of Virtual Inheritance. And a disclaimer before we proceed, the examples considered here are just hypothetical cases and not to disparage anyone.
Further extending the inheritance hierarchy mentioned in part 1, consider that now the company has decided to introduce an open source mobile OS, based on existing "Mobile_OS
" of the company but enhancing it further to favor open source development with a strong eco-system of contributors and named it "Android
".
class Mobile_OS
{
public:
float iKernalVersion;
float iReleaseVersion;
char* strVendor ;
virtual float GetKernalVersion();
virtual float GetReleaseVerison();
Mobile_OS();
~ Mobile_OS();
};
class Android : public Mobile_OS
{
private:
char* strVendor;
char* strProjectCode ;
public:
int iAndroidCustomRomVersion ;
float GetKernalVersion();
float GetReleaseVerison();
Android ();
~ Android ();
};
The memory layout of Android class will be exactly the same as of "WindowsPhone
" class mentioned earlier, since both of these two classes are derived from the same base "Mobile_OS
".
The design looks good till this point, with reusable modules and well defined class hierarchy. Now consider the same company which had produced Windows and Android phones now has plans to come up with "Tablet" devices, with a hybrid operating system, based on superior features of both Windows and Android phones and making itself a unique OS by adding exclusive unique features and calls it " Hybrid_Tablet".
Do You Foresee Any Problems With This Design?
Yes, we run into issues of data redundancy and inconsistency by deriving a " Hybrid_Tablet " from " WindowsPhone" and "Android" classes.
What Caused Issues of Data Redundancy and Inconsistency?
The cause of these issues is rooted in the fact that, both "WindowsPhone
" and "Android
" classes are derived from a common base "Mobile_OS
" and hence both of these classes persist their own instance of "Mobile_OS
" class leading to issues of data redundancy and inconsistency.
How Expensive is this Fault?
It depends on the size of the base class, in our case, it is the size of "Mobile_OS
" class. Even if the developer cautiously resolves issues of data inconsistency by object reference for class data access, there is no solution to curb data redundancy. And if the base class is huge with many data members, this design decision proves expensive.
What is the Solution?
The only solution to problems like the above is to use Virtual Inheritance.
In a system of classes, belonging to a single hierarchy, if a class is derived from multiple base classes which are in-turn derived from a single base class, the concept of Virtual Inheritance is used to ensure that there is only a single copy of the common base class in the most derived class.
Deploying virtual inheritance in the current issue, we can ensure that the "Hybrid_Tablet
" will have only a single instance of "Mobile_OS
" which is commonly shared between "WindowsPhone
" and "Android
".
To inform the compiler to use single instance of "Mobile_OS
" in " Hybrid_Tablet
" the two base classes of " Hybrid_Tablet
" which are "WindowsPhone
" and "Android
" should be derived virtually from "Mobile_OS
" as shown below:
class Mobile_OS
{
public:
float iKernalVersion;
float iReleaseVersion;
char* strVendor ;
virtual float GetKernalVersion();
virtual float GetReleaseVerison();
Mobile_OS();
~ Mobile_OS();
};
class WindowsPhone : public virtual Mobile_OS
{
private:
char* strCodeName ;
char* iHardwarePlatform ;
public:
int iCustomRomVersion ;
float GetKernalVersion();
float GetReleaseVerison();
WindowsPhone();
~ WindowsPhone();
};
class Android : public virtual Mobile_OS
{
private:
char* strVendor;
char* strProjectCode ;
public:
int iAndroidCustomRomVersion ;
float GetKernalVersion();
float GetReleaseVerison();
Android ();
~ Android ();
};
Memory Layout of an Object in Case of Virtual Inheritance
In the case of non virtual inheritance, it is most certainly accepted design practice that both base class and derived class will have the same starting address since in a derived class the base instance is placed first. For more details, please refer to Part 1.
In the case of virtual inheritance, embedded base virtually floats within the derived object without having a definite fixed displacement. Hence, there is an overhead of maintaining this information within the derived virtual object and this is achieved by maintaining "Virtual Base Table Pointer" and "Virtual Base Pointer Table" as shown in the diagram below:
Click on the image for high resolution version
The instance of each virtually derived class will have a hidden pointer "vbptr
" which is an acronym for "Virtual Base Table Pointer" which points to "Virtual Base Pointers Table" of a class. The Virtual Base Pointers Table contains displacement of the virtual base within the derived class from the address point of "vbptr
" in number of bytes.
In the above example, "Android::vbptr
" points to "Virtual Base Pointers Table" which has two entries that are displacement values for virtual base, and the acronyms stand for:
And_Dt_And_vbptr_And = In Android Instance Distance of Android vbptr to Android
And_Dt_And_vbptr_Mos = In Android Instance Distance of Android vbptr to Mobile_OS
Memory Layout of Most Derived Class in Virtual Inheritance
Now, coming back to our original issue of designing a "Hybrid_Tablet
" by deriving from multiple base classes which are derived from common base class, can be addressed using virtual inheritance. And this is achieved by:
- Deriving "
WindowsPhone
" and "Android
" virtually from "Mobile_OS
", and - Deriving "
Hybrid_Tablet
" from "WindowsPhone
" and "Mobile_OS
".
class Hybrid_Tablet : public WindowsPhone, public Android
{
Private:
..................
..................
public:
..................
..................
};
As mentioned earlier, in virtual inheritance, the position of base class instance is arbitrary within derived class, and in this case, we can see that, instance of "Hybrid_Tablet
" has only a single instance of "Mobile_OS
", "WindowsPhone
" and "Android
" and reference to "Mobile_OS
" is maintained with the help of "Virtual Base Table Pointer", one for each class and which points to a single "Virtual Base Pointers Table"of "Hybrid_Tablet
" class as shown below:
Click on the image for high resolution version
Hbt_Dt_Wnp_vbptr_Wnp = In Hybrid_Tablet Instance Distance of WindowsPhone to WindowsPhone
Hbt_Dt_Wnp_vbptr_Mos = In Hybrid_Tablet Instance Distance of WindowsPhone to Mobile_OS
Hbt_Dt_And_vbptr_And = In Hybrid_Tablet Instance Distance of Android to Android
Hbt_Dt_And_vbptr_Mos = In Hybrid_Tablet Instance Distance of Android to Mobile_OS
With this, I hope this article delineates moderately deeper insight of virtual inheritance and its implementation details. Kindly email me if you have any queries or suggestions.
Also, please let me know if you are interested in digging deeper into virtual inheritance for understanding "Data Access" and "Function Calling" mechanisms, I can write a post on that as well.
E- Mail: mail2vijaydr@gmail.com
References: MSDN, CodeProject, CodeGuru, and other web resources.