Inheritance in C++

Programming Workshop 2 (CSCI 1061U)

Faisal Qureshi

Faculty of Science, UOIT

http://vclab.science.uoit.ca


Inheritance

Inheritance is an important concept in Object Oriented Programm (OOP). It facilitates abstraction. Inheritance is used as follows. First a general form of class is defined. Next, specialized classes are inherited from this general form. Specialized forms add the necessary functionality to the generalized from.

Consider the following code. We begin by defining a general class Person.

person.h

class Person
{
public:
    Person(string name, int age) { ... }

    friend ostream& operator<<(ostream& os, const Person& p) { ... }

    string name;
    int age;
};

We can now define a class Student, which is a Person, yet it also adds functionality to the Person class. Specifically, a student also has a grade. Notice that the Person doesn't have a grade.

class Student : public Person 
{
public:
    Student(string name, int age, int grade);

    friend ostream& operator<<(ostream& os, const Student& s);

    int grade;
};

Similarly, we can inherit a class Teacher from class Person. A Teacher is a Person.

class Teacher : public Person 
{
public:
    Teacher(string name, int age, string subject, float salary);

    friend ostream& operator<<(ostream& os, const Student& s);

    float salary;
    string subject;
};

A Teacher has a salary and a subject in addition to name and age.

In the above example, we use class Student : public Person and class Teacher : public Person to indicate Student and Teacher classes are inherited from Person class.

Here Person class is the base class. Student and Teacher classes are derived classes. Derived classes automatically get member variables and member functions of the base class, which facilitates code/functionality reuse. As you can see, we can also add additional members functions and variables in the derived class. We can also redefine member functions (already present in the base class) in the derived class.

Inheritance sets up an is a relationship. In the above example, a Student is a person. Note, however, that Person is not a student. Since a Student is a Person, the following is allowed

Person* a = new Student(...);

This has important implications, which will come to later.

Common terms

Constructors and Destructors

Base class constructors are not inherited in derived classes, and these must initialize all base class member variables. The derived class constructor can use base class constructors to initialize base class member variables

Base methods that are not inherited

Similar to constructors, both desctructors and copy constructors are not inherited from the base method. Assignment operator is not inherited from the base class as well.

Destructors

When a derived class's destructor are called, the base class destructor is automatically called. This means that the derived class destructor only need to concern itself with the derived class variables.

Consider the following situation. B inherits A, and C inherits B. When C is destroyed (say by going out of scope), first C destructor is called, then B destructor is called, and then A destructor is called.

Private members of base class

Private member variables can ONLY be accessed "by name" in member functions of the class they’re defined in. This means that while the derived class inherits the private members of the base class, the derived class still cannot directly access these.

Similarly, private functions of base class cannot be called directly in the derived class.

class A
{
    public:
    A() { ... }
    
    private:
    boo() { ... }
    
    int x;
};

class B : public A
{
    public:
    B() { ... }
    
    void foo() {
        cout << x << endl; // Compiler error, since x is a private member of 
                           // base class A
        boo();             // Compiler error, since boo is a private member
                           // of base class A
    }
}

How to access private members of base class

One can access private members in a base class by using non-private functions provided in the base class that manipulate the relevant private members.

Protected members of base class

Protected members of base class are directly accessible in a derived class; however, these are not accessible outside the base/derived classes. Some feel that this violates information hiding principle.

Recall that public members of a class are directly accessible any where.

class A
{
    public:
    A() { ... }
    
    protected:
    boo() { ... }
    
    int x;
};

class B : public A
{
    public:
    B() { ... }
    
    void foo() {
        cout << x << endl; // Ok, since x is a protected member of 
                           // base class A
        boo();             // Ok, since boo is a private member
                           // of base class A
    }
}

int main()
{
    A a;
    a.boo()               // Compiler error, since boo is a 
                          // protected member of class A.
    cout << a.x << endl;  // Compiler error, since x is a protected
                          // of class A.
}

Protected and Private Inheritance

The following code shows a typical public inheritance:

class A: public B 
{ ... }

Here class A inherits B.

For protected inheritance we will use

class A: protected B 
{ ... }

Here public members in the base class B will become protected members in derived class A.

For private inheritance we will use

class A: private B 
{ ... }

All members in the base class B will become private members in derived class A.

Multiple Inheritance

A derived class can have more than one base class. In the code below class G inherits classes H, I, and J.

class G: public H, I, J
{ ... };

Use this feature with care, since the possibility for ambiquities are endless.

References