Base Classes and Derived Classes in C++

Inheritance enables you to define a general class (i.e., a base class) and later extend it to more specialized classes (i.e., derived classes).

You use a class to model objects of the same type. Different classes may have some com­mon properties and behaviors, which can be generalized in a class that can be shared by other classes. Inheritance enables you to define a general class and later extend it to more special­ized ones. The specialized classes inherit properties and functions from the general class.

Consider geometric objects. Suppose you want to design the classes to model geomet­ric objects like circles and rectangles. Geometric objects have many common properties and behaviors. They can be drawn in a certain color, filled or unfilled. Thus, a general class GeometricObject can be used to model all geometric objects. This class contains the prop­erties color and filled and their appropriate get and set functions. Assume that this class also contains the toString() function, which returns a string representation for the object. Since a circle is a special type of geometric object, it shares common properties and functions with other geometric objects. Thus, it makes sense to define the Circle class that extends the GeometricObject class. Likewise, Rectangle can also be defined as a derived class of GeometricObject. Figure 15.1 shows the relationships among these classes. A trian­gle pointing to the base class is used to denote the inheritance relationship between the two classes involved.

In C++ terminology, a class C1 extended from another class C2 is called a derived class, and C2 is called a base class. We also refer to a base class as a parent class or a superclass and to a derived class as a child class or a subclass. A derived class inherits accessible data fields and functions from its base class and may also add new data fields and functions.

The Circle class inherits all accessible data fields and functions from the GeometricObject class. In addition, it has a new data field, radius, and its associated get and set functions. It also contains the getArea(), getPerimeter(), and getDiameter() functions for returning the area, perimeter, and diameter of the circle.

The Rectangle class inherits all accessible data fields and functions from the GeometricObject class. In addition, it has data fields width and height and its associated get and set functions. It also contains the getArea() and getPerimeter() functions for returning the area and perimeter of the rectangle.

The class definition for GeometricObject is shown in Listing 15.1. The preprocessor directives in lines 1 and 2 guard against multiple inclusions. The C++ string class header is included in line 3 to support the use of the string class in GeometricObject. The isFilled() function is the accessor for the filled data field. Since this data field is the bool type, the accessor function is named isFilled() by convention.

Listing 15.1 GeometricObject.h

1 #ifndef GEOMETRICOBJECT_H
2
#define GEOMETRICOBJECT_H
3
#include <string>
4
using namespace std;

5
6
class GeometricObject
7 {
8     
public:
9     GeometricObject();
10    GeometricObject(
const string& color, bool filled);
11    string getColor()
const;
12   
void setColor(const string& color);
13   
bool isFilled() const;
14   
void setFilled(bool filled);
15    string toString()
const;
16
17   
private:
18    string color;
19   
bool filled;
20 };
// Must place semicolon here
21
22
#endif

Figure 15.1 The GeometricObject class is the base class for Circle and Rectangle.

The GeometricObject class is implemented in Listing 15.2. The toString function (lines 35-38) returns a string that describes the object. The string operator + is used to con­catenate two strings and returns a new string object.

Listing 15.2 GeometricObject.cpp

1 #include “GeometricObject.h”
2
3 GeometricObject::GeometricObject()
4 {
5    color =
“white”;
6    filled =
false;
7 }
8
9 GeometricObject::GeometricObject(
const string& color, bool filled)
10 {
11   
this->color = color;
12   
this->filled = filled;
13 }
14
15 string GeometricObject::getColor()
const
16 {
17   
return color;
18 }
19
20
void GeometricObject::setColor(const string& color)
21 {
22   
this->color = color;
23 }
24
25
bool GeometricObject::isFilled() const
26 {
27   
return filled;
28 }
29
30
void GeometricObject::setFilled(bool filled)
31 {
32   
this->filled = filled;
33 }
34
35 string GeometricObject::toString()
const
36 {
37   
return “Geometric Object”;
38 }

The class definition for Circle is shown in Listing 15.3. Line 5 defines that the Circle class is derived from the base class GeometricObject. The syntax

tells the compiler that the class is derived from the base class. So, all public members in GeometricObject are inherited in Circle.

Listing 15.3 DerivedCircle.h

1 #ifndef CIRCLE_H
2
#define CIRCLE_H
3
#include “GeometricObject.h”
4
5
class Circle: public GeometricObject

6 {
7
public:
8     Circle();
9     Circle(
double);
10    Circle(
double radius, const string& color, bool filled);
11   
double getRadius() const;
12   
void setRadius(double);
13   
double getArea() const;
14   
double getPerimeter() const;
15   
double getDiameter() const;
16    string toString()
const;
17
18
private:
19   
double radius;
20 };
// Must place semicolon here
21
22
#endif

The Circle class is implemented in Listing 15.4.

Listing 15.4 DerivedCircle.cpp

1 #include “DerivedCircle.h”
2
3
// Construct a default circle object
4 Circle::Circle()
5 {
6    radius =
1;
7 }
8
9
// Construct a circle object with specified radius
10 Circle::Circle(double radius)
11 {
12    setRadius(radius);
13 }
14
15
// Construct a circle object with specified radius,
16 // color and filled values
17 Circle::Circle(double radius, const string& color, bool filled)
18 {
19    setRadius(radius);
20    setColor(color);
21    setFilled(filled);
22 }
23
24
// Return the radius of this circle
25 double Circle::getRadius() const
26 {
27   
return radius;
28 }
29
30
// Set a new radius
31 void Circle::setRadius(double radius)
32 {
33   
this->radius = (radius >= 0) ? radius : 0;
34 }
35
36
// Return the area of this circle
37 double Circle::getArea() const
38 {
39    return radius * radius * 3.14159;
40 }
41

42 // Return the perimeter of this circle

43 double Circle::getPerimeter() const

44 {

45    return 2 * radius * 3.14159;
46 }
47

48  // Return the diameter of this circle

49 double Circle::getDiameter() const

50 {
51    return 2 * radius;
52 }
53
54 // Redefine the toString function
55 string Circle::toString() const
56 {

57    return “Circle object”;
58 }

The constructor Circle(double radius, const string& color, bool filled) is implemented by invoking the setColor and setFilled functions to set the color and filled properties (lines 17-22). These two public functions are defined the base class GeometricObject and are inherited in Circle. So, they can be used in the derived class.

You might attempt to use the data fields color and filled directly in the constructor as follows:

Circle::Circle(double radius, const string& c, bool f)

{

this->radius = radius; // This is fine

color = c; // Illegal since color is private in the base class

filled = f; // Illegal since filled is private in the base class

}

This is wrong, because the private data fields color and filled in the GeometricObject class cannot be accessed in any class other than in the GeometricObject class itself. The only way to read and modify color and filled is through their get and set functions.

The class Rectangle is defined in Listing 15.5. Line 5 defines that the Rectangle class is derived from the base class GeometricObject. The syntax

tells the compiler that the class is derived from the base class. So, all public members in GeometricObject are inherited in Rectangle.

Listing 15.5 DerivedRectangle.h

1 #ifndef RECTANGLE_H
2
#define RECTANGLE_H
3
#include “GeometricObject.h”
4

5 class Rectangle: public GeometricObject
6 {
7
public:
8    Rectangle();
9    Rectangle(
double width, double height);
10   Rectangle(
double width, double height,
11   
const string& color, bool filled);
12   
double getWidth() const;
13   
void setWidth(double);
14   
double getHeight() const;
15   
void setHeight(double);
16   
double getArea() const;
17   
double getPerimeter() const;
18   string toString()
const;
19
20
private:
21   
double width;
22   
double height;
23 };
// Must place semicolon here
24
25
#endif

The Rectangle class is implemented in Listing 15.6.

Listing 15.6 DerivedRectangle.cpp

1 #include “DerivedRectangle.h”
2
3
// Construct a default rectangle object
4 Rectangle::Rectangle()
5 {
6    width =
1;
7    height =
1;
8 }
9
10
// Construct a rectangle object with specified width and height
11 Rectangle::Rectangle(double width, double height)
12 {
13    setWidth(width);
14    setHeight(height);
15 }
16
17 Rectangle::Rectangle(
18
double width, double height, const string& color, bool filled)
19 {
20    setWidth(width);
21    setHeight(height);
22    setColor(color);
23    setFilled(filled);
24 }
25
26
// Return the width of this rectangle
27 double Rectangle::getWidth() const
28 {
29   
return width;
30 }
31
32
// Set a new radius

33 void Rectangle::setWidth(double width)
34 {
35   
this->width = (width >= 0) ? width : 0;
36 }
37
38
// Return the height of this rectangle
39 double Rectangle::getHeight() const
40 {
41   
return height;
42 }
43
44
// Set a new height
45 void Rectangle::setHeight(double height)
46 {
47   
this->height = (height >= 0) ? height : 0;
48 }
49
50
// Return the area of this rectangle
51 double Rectangle::getArea() const
52 {
53   
return width * height;
54 }
55
56
// Return the perimeter of this rectangle
57 double Rectangle::getPerimeter() const
58 {
59   
return 2 * (width + height);
60 }
61
62
// Redefine the toString function, to be covered in Section 15.5
63 string Rectangle::toString() const
64 {
65   
return “Rectangle object”;
66 }

Listing 15.7 gives a test program that uses these three classes—GeometricObject, Circle, and Rectangles.

Listing 15.7 TestGeometricObject.cpp

1 #include “GeometricObject.h”
2 #include “DerivedCircle.h”
3 #include “DerivedRectangle.h”
4 #include <iostream>
5
using namespace std;
6
7
int main()
8 {
9     GeometricObject shape;
10    shape.setColor(
“red”);
11    shape.setFilled(
true);
12    cout << shape.toString() << endl
13    <<
” color: ” << shape.getColor()
14    <<
” filled: ” << (shape.isFilled() ? “true” : “false”) << endl;
15
16    Circle circle(
5);
17    circle.setColor(
“black”);
18    circle.setFilled(
false);
19    cout << circle.toString()<< endl
20    <<
” color: ” << circle.getColor()

21    << ” filled: ” << (circle.isFilled() ? “true” : “false”)
22    <<
” radius: ” << circle.getRadius()
23    <<
” area: ” << circle.getArea()
24    <<
” perimeter: ” << circle.getPerimeter() << endl;
25
26    Rectangle rectangle(
2, 3);
27    rectangle.setColor(
“orange”);
28    rectangle.setFilled(
true);
29    cout << rectangle.toString()<< endl
30    <<
” color: ” << circle.getColor()
31    <<
” filled: ” << (circle.isFilled() ? “true” : “false”)
32    <<
” width: ” << rectangle.getWidth()
33    <<
” height: ” << rectangle.getHeight()
34    <<
” area: ” << rectangle.getArea()
35    <<
” perimeter: ” << rectangle.getPerimeter() << endl;
36
37   
return 0;
38 }

The program creates a GeometricObject and invokes its functions setColor, setFilled, toString, getColor, and isFilled in lines 9-14.

The program creates a Circle object and invokes its functions setColor, setFilled, toString, getColor, isFilled,  getRadius, getArea, and getPerimeter in lines 16-24. Note that the setColor and setFilled functions are defined in the GeometricObject class and inherited in the Circle class.

The program creates a Rectangle object and invokes its functions setColor, setFilled, toString, getColor, isFilled, getWidth, getHeight, getArea, and getPerimeter in lines 26-35. Note that the setColor and setFilled functions are defined in the GeometricObject class and inherited in the Rectangle class.

Note the following points about inheritance:

  • Private data fields in a base class are not accessible outside the class. Therefore, they cannot be used directly in a derived class. They can, however, be accessed/mutated through public accessor/mutator if defined in the base class.
  • Not all is-a relationships should be modeled using inheritance. For example, a square is a rectangle, but you should not define a Square class to extend a Rectangle class, because there is nothing to extend (or supplement) from a rectangle to a square. Rather you should define a Square class to extend the GeometricObject For class A to extend class B, A should contain more detailed information than B.
  • Inheritance is used to model the is-a Do not blindly extend a class just for the sake of reusing functions. For example, it makes no sense for a Tree class to extend a Person class, even though they share common properties such as height and weight. A derived class and its base class must have the is-a relationship.

C++ allows you to derive a derived class from several classes. This capability is known as multiple inheritance, which is discussed in Supplement IV.A.

Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *