Objective:
After performing this lab, students will practice:
- Diamond Problem
- Pure Virtual functions and Abstract classes
- Implement multiple and virtual inheritance
Activity 1:
For this activity, we are going to work on a classical multiple inheritance issue known as ‘diamond problem’.
- Create classes: Faculty, Administrator, Teacher, & AdministratorTeacher.
- Faculty inherits Administrator and Teacher. While AdministratorTeacher has two parents Administrator and Teacher which represents that an Administrator can be a Teacher and vice versa.
- Add a print() method to Faculty, Administrator, and Teacher which displays the class name.
- In the driver, create a pointer array of 3 Faculty objects.
- Create one object for each of the remaining three classes as well and assign these three object to the Faculty object array.
- Now, in a loop call the print method on the Faculty object array and observe the code behavior.
- You may observe that “Faculty” is displayed on the console 3 times which is wrong.
- To make corrections, use polymorphism. Make the print() method virtual and execute again.
- This time you will encounter an error. It occurs because the AdministratorTeacher object shows ambiguous behavior when calling the print() method (It does not know which print method it should call).
- To resolve this issue, we are going to use virtual inheritance. First, make the print() method pure virtual in Faculty. And add a print() method in the AdministratorTeacher class as well. Qualify or override this print() method by calling the print() of either Teacher or Administrator specifically.
- Now, use virtual inheritance i.e. declare Faculty inheritance using public virtual keyword for Teacher and Administrator classes.
- Execute the program again. This time you will observe that correct class names are displayed on console.
- Each faculty member is assigned an id by the university. To represent this, add an get_id() method to both Administrator and Teacher which returns a unique integer number.
- Call the get_id() method using the AdministratorTeacher object. Observe the error. Can you explain why?
- One approach would be to qualify the get_id() method as we did with print(). But if we are making use of inheritance, there should only be one get_id() method in the Faculty class. Remove the get_id() method from the child classes. Execute the program and observe that it works. Can you explain why is there no ambiguity when calling the get_id() method from AdministratorTeacher object?
Solution:
#include<iostream>
using namespace std;
class Faculty
{
public:
virtual void print() = 0;
int get_id()
{
return 2023;
}
};
class Administrator : public virtual Faculty
{
public:
void print()
{
cout << "Administrator Class" << endl;
}
};
class Teacher : public virtual Faculty
{
public:
void print()
{
cout << "Teacher Class" << endl;
}
};
class AdministratorTeacher : public Administrator, public Teacher
{
public:
void print()
{
cout << "AdministratorTeacher Class" << endl;
}
};
int main()
{
Faculty* objects[3];
Administrator obj1;
Teacher obj2;
AdministratorTeacher obj3;
objects[0] = &obj1;
objects[1] = &obj2;
objects[2] = &obj3;
for (int i = 0; i < 3; i++)
{
objects[i]->print();
cout << "ID: " << objects[i]->get_id() << endl;
}
return 0;
}
Activity 2:
Define a pure abstract base class called BasicShape. The BasicShape class should have the following members:
Private Member Variable:
area, a double used to hold the shape's area.
Public Member Functions:
getArea.:
This function should return the value in the member variable area.
calcArea.:
This function should be a pure virtual function.
Next, define a class named Circle. It should be derived from the BasicShape class. It should have the following members:
Private Member Variables:
centerX, a long integer used to hold the x coordinate of the circle’s center.
centerY, a long integer used to hold the y coordinate of the circle’s center.
radius, a double used to hold the circle's radius.
Public Member Functions: constructor—accepts values for centerX, centerY, and radius.
Should call the overridden calcArea function described below.
getCenterX—returns the value in centerX.
getCenterY—returns the value in centerY.
calcArea—calculates the area of the circle(area = 3.14159 * radius * radius) and stores the result in the inherited member area.
Next, define a class named Rectangle. It should be derived from the BasicShape class. It should have the following members:
Private Member Variables:
width, a long integer used to hold the width of the rectangle.
length,a long integer used to hold the length of the rectangle.
Public Member Functions:
constructor—accepts values for width and length. Should call the overridden calcArea function described below.
getWidth—returns the value in width.
getLength—returns the value in length.
calcArea—calculates the area of the rectangle (area = length * width) and stores the result in the inherited member area.
After you have created these classes, create a driver program that defines a Circle object and a Rectangle object. Demonstrate that each object properly calculates and reports its area.
Solution:
#include<iostream>
using namespace std;
class BasicShape
{
protected:
double area;
public:
double getArea()
{
return area;
}
virtual void calcArea() = 0;
};
class Circle : public BasicShape
{
private:
int centerX;
int centerY;
double radius;
public:
Circle(int x, int y, double r)
{
centerX = x;
centerY = y;
radius = r;
}
int CenterX()
{
return centerX;
}
int CenterY()
{
return centerY;
}
void calcArea()
{
area = 3.14159 * radius * radius;
}
};
class Rectangle : public BasicShape
{
private:
int width;
int length;
public:
Rectangle(int w, int l)
{
width = w;
length = l;
}
int getWidth()
{
return width;
}
int getLength()
{
return length;
}
void calcArea()
{
area = length * width;
}
};
int main()
{
Circle circle(2, 3, 4.5);
circle.calcArea();
cout << "Circle Area: " << circle.getArea() << endl;
cout << "Center: (" << circle.CenterX() << ", " << circle.CenterY() << ")" << endl;
Rectangle rectangle(5, 7);
rectangle.calcArea();
cout << "Rectangle Area: " << rectangle.getArea() << endl;
cout << "Width: " << rectangle.getWidth() << endl;
cout << "Length: " << rectangle.getLength() << endl;
return 0;
}