Inheritance

An important feature of C++ classes and structures is “inheritance,” which works very similarly to how it sounds. Essentially, classes can “inherit” functionality from other classes, just as humans can inherit characteristics from their parents. A common example of inheritance in programming is with shapes:

#include <iostream>

class Quadrilateral {
    public:
        int numberOfSides = 4;
};

class Square : public Quadrilateral {
    private:
        int length;
    
    public:
        Square(int length) {
            this->length = length;
        }
        int getArea() {
            return length * length;
        }
};

class Rectangle : public Quadrilateral {
    private:
        int width;
        int height;
    
    public:
        Rectangle(int width, int height) {
            this->width = width;
            this->height = height;
        }
        int getArea() {
            return width * height;
        }
};

int main() {
    Square square(5);
    std::cout << "A square has " << square.numberOfSides << " sides.\n";
    std::cout << "Square area: " << square.getArea() << "\n\n";

    Rectangle rectangle(3, 6);
    std::cout << "A rectangle has " << rectangle.numberOfSides << " sides.\n";
    std::cout << "Rectangle area: " << rectangle.getArea() << "\n";

    return 0;
}

Program output:

A square has 4 sides.
Square area: 25

A rectangle has 4 sides.
Rectangle area: 18

A “derived” class (Rectangle and Square in the code above) can inherit from a “base” class (Quadrilateral) when we put a colon after the derived class name followed by an access modifier and the base class name.

When a derived class inherits from a base class, we say that the derived class is a base class. For example, a Rectangle is a Quadrilateral and a Square is a Quadrilateral. A derived class contains all code from its base class (with some exceptions that we’ll talk about when going over access modifiers). This means that the Square and Rectangle classes in the sample program both have their own numberOfSides variable that’s equal to 4.

Inheritance is useful when multiple classes share some set of variables and functions. Rather than copying and pasting those variables/functions into each class that uses them, they can be placed into just one base class. In the sample program, the numberOfSides variable could be placed into each of its derived classes, but it’s easier to only write it once (in the Quadrilateral class).

Here’s a visualization of this program’s class hierarchy:

Access Modifiers

Recall that public variables/functions in a class are accessible outside of their class, while private variables/functions are not. Variables and functions can also be protected, meaning that they’re only accessible to derived classes.

Access modifiers are also used to represent different types of inheritance when placed before base class names. In the above sample program, both the Square class and the Rectangle class follow public inheritance because of “ : public Quadrilateral“. Inheritance can also be protected or private.

The access modifier used for inheritance determines the access level of variables and functions in the base class. Most likely, you’ll almost always use public inheritance, but here are some tables that show the effects of each type of inheritance:

Public Inheritance
Access Level in Base ClassAccess Level in Derived Class
PrivateInaccessible
ProtectedProtected
PublicPublic
  • The derived class cannot access any private variables/functions in the base class.
  • Any protected variables/functions in the base class are protected for the derived class.
  • Any public variables/functions in the base class are public for the derived class.
Protected Inheritance
Access Level in Base ClassAccess Level in Derived Class
PrivateInaccessible
ProtectedProtected
PublicProtected
  • The derived class cannot access any private variables/functions in the base class.
  • Any protected variables/functions in the base class are protected for the derived class.
  • Any public variables/functions in the base class are protected for the derived class.
Private Inheritance
Access Level in Base ClassAccess Level in Derived Class
PrivateInaccessible
ProtectedPrivate
PublicPrivate
  • The derived class cannot access any private variables/functions in the base class.
  • Any protected variables/functions in the base class are private for the derived class.
  • Any public variables/functions in the base class are private for the derived class.

Calling a Base Class Constructor

Whenever the constructor of a derived class runs, it also runs the default constructor of the base class, automatically. However, you may sometimes want to call a non-default base class constructor (one that has parameters). This can be done in the derived class’s initializer list:

#include <iostream>

class Quadrilateral {
    public:
        int numberOfSides = 4;
};

class Rectangle : public Quadrilateral {
    protected:
        int width;
        int height;
    
    public:
        Rectangle(int width, int height) {
            this->width = width;
            this->height = height;
        }
        int getArea() {
            return width * height;
        }
};

class Square : public Rectangle {
    public:
        Square(int length) : Rectangle(length, length) {}
};

int main() {
    Square square(5);
    std::cout << "A square has " << square.numberOfSides << " sides.\n";
    std::cout << "Square area: " << square.getArea() << "\n\n";

    Rectangle rectangle(3, 6);
    std::cout << "A rectangle has " << rectangle.numberOfSides << " sides.\n";
    std::cout << "Rectangle area: " << rectangle.getArea() << "\n";

    return 0;
}

This program does the same thing as the first, except it follows a different inheritance structure. Now, the Rectangle class inherits from the Quadrilateral class, and the Square class inherits from the Rectangle class. (A rectangle is a quadrilateral, and a square is a rectangle). The class hierarchy looks like this:

You can see that we got rid of more repetitive code by just using the rectangle’s getArea function for getting the area of a square. This works because, in the Square constructor, we call the constructor for the Rectangle class, setting its width and height both equal to the side length of the square. Then, when doing square.getArea(), the rectangle’s getArea function returns the product of its width (5) and height (5).

Notice how the Rectangle class’s width and height variables are now “protected.” This allows the Square class to access those variables if ever needed.