Skip to Content

C++: Inheritance

NOTE: This page still being created…

Recall: Three main OO ideas:

Object Orientation is the combination of three main ideas:

  • Encapsulation: this means putting things together to make it look like a whole and to hide the internal things from external inspectors outside.
  • Inheritance: this means that some types can be subtypes of other things and inherit their features. For this, always think of the phrase “is-a”. For example, a bear is-a mammal. Bears inherit features of mammals (all mammals have hair; bears have hair).
  • Polymorphism: this means that things in your program can act differently based on the context. (Greek “poly” means many, “morph” means form; so the word means “many forms”)

Inheritance

Inheritance is central to object oriented programming. It is the capability that allows us to avoid duplicating code when defining similar things in our program.

Consider an example: suppose we have a Customer class and we use it to represent all of our customers in some business application. Suppose then that we need to treat some customers as special; we will call them a VIPCustomer. The thing is, most of the attributes (data) and operations (methods) of our plain Customer will be applicable to a VIPCustomer, so we do not want to just duplicate all that code.

The key insight is: a VIPCustomer IS A Customer! So the class VIPCustomer should inherit all the stuff in the Customer class, and then add to it and perhaps slightly modify some of it. In other words, VIPCustomer is a subclass of Customer.

Inheritance is an IS-A relationship! If class A inherits from class B, then an object of class A is an object of class B. There are not two objects involved, the object is an object of both classes.

Take an example from biology: you are a Human; you are also a Mammal; you are one object, but you are an object of both the Human class and the Mammal class. Human inherits from Mammal. Human is a subclass of Mammal. All humans are mammals, but not all mammals are humans. Squirrel is also a subclass of Mammal. All squirrels are mammals.

Inheritance is a relationship between two classes – the subclass defines a more specific subset of objects than the base class, which is more generic. The base class Mammal is more generic than the subclass Human. The subclass is sometimes called the derived class. The base class can be called the parent class, and is often referred to as the superclass. In fact, Java has a keyword super; C++ does not because it allows more than one superclass, and so each superclass must be named explicitly using scope resolution (e.g., a SuperClass:: prefix).

So, inheritance gives us a conceptual mechanism for code reuse: create generic code and data for the general case (base class), then through inheritance use it and adapt it to specific contexts (the subclasses).

Coding Mechanisms

Java has two keywords for inheritance: extends and implements. For now we are talking about extends inheritance, though later we will talk about implements.

In Java, when declaring a class with inheritance you would write:

public class VIPCustomer extends Customer
{
...
}

In C++, this looks like:

class VIPCustomer : public Customer
{
...
};

There is no keyword for inheritance, only the presence of the colon (:) operator, and a class name (or more) after it. The public keyword is an access modifier that specifies the maximum access allowed. In the above usage, everything that makes up the Customer class is included in the VIPCustomer class. If it is public in Customer it is public in VIPCustomer. If it is private in Customer, it is still private in VIPCustomer. The public/private/protected modifiers can be used in the inheritance specification, so that, for example:

class VIPCustomer : private Customer
{
...
};

would make everything in Customer private, no matter what access it is originally. If we use protected as the inheritance access modifier, public things become protected, but things that are already protected or private remain as they were. The default access modifier is private, so

class VIPCustomer : Customer
{
...
};

is the same as using private.

Generalized and Specialized Capabilities

Through inheritance, a subclass includes all of the attributes and methods of its parent class—this is generalized capabilities that are suitable for the parent class, and probably suitable for the subclasses. But sometimes it can be important to define specialized versions of the parent methods. Think about a display (or print) method; the generic display of a parent class is probably not suitable for the subclass.

When a subclass defines a specialized version of a parent’s method, we call this an override. Override means that the subclass defines its own version of the method; when that method is called on an object of the subclass, that specific version of the method will be used rather than the one defined in the base class.

C++ Virtual Methods (member functions)

In Java, when a subclass inherits from a base class, it is automatically allowed to override any methods of the base class. In C++, this is not automatic. Methods are only allowed to be overridden if they are originally defined as virtual. So a base class has to be written in a way that expects and allows inheritance and method overriding.

Here’s an example:

class Customer
{
 public:
   std::string salutation();
   virtual std::string nickname();
};
class VIPCustomer : Customer
{
 public:
   std::string salutation();  // NOT allowed!
   std::string nickname();    // allowed
};

Because salutation() was not declared virtual in the base class, it is not allowed to be declared in the subclass. C++ has also introduced the keyword override to explicitly tell the compiler that you know that you are overriding a method. Similarly, Java introduced the @Override annotation for the same purposes. This just helps make your code safer. So a further example is below:

class Customer
{
 public:
   std::string salutation();
   virtual std::string nickname();
};
class VIPCustomer : Customer
{
 public:
   virtual std::string nickname() override;   // allowed
   virtual int daysVIP() override;            // NOT allowed!
};

In the above, the override says that there must be the same function in the base class, so the second fails. Also, we added virtual in front, because if a function is virtual in the base class, it probably should be virtual in the subclass, so that an even further sub-subclass can be defined, if needed.

Virtual Destructors

If a class has any virtual methods, meaning it can be subclassed, it is a good idea to declare the destructor as virtual, so that the subclass can declare its own destructor that will work anywhere in the code.

Multiple vs. Single Inheritance

Java uses the keyword extends for inheritance. But Java only lets you extend one class! This is called single inheritance. This is a very safe inheritance model, and it is a good design choice. C++, however, allows multiple inheritance – you can have a list of classes after the colon (:), and your class will inherit from all of them! This can be dangerous and ambiguous, and should be avoided as much as possible, unless you are designing interfaces (see below in the interface section).

Abstract Classes

An abstract class is one that does not allow objects of it to be created. Huh? I thought that was the whole point of OOP? Yes, but an abstract class can be subclassed, and then objects of the subclass can be created, and since inheritance is an IS-A relationship, then the object of the subclass is an object of the abstract class! Voila!

Think about biology again. Mammal is an abstract class; nothing is just a mammal, it is always an animal that is a specific species that is a subclass of Mammal, like a Squirrel or a Human.

Java has the keyword abstract to declare a class as abstract, and Java interfaces are always abstract, but C++ does not have a keyword for this. C++ supports abstract classes this way: if a class has at least one method that is unimplemented, then it is abstract. What does this look like? You just write “=0” after the method interface, rather than a body. Like this:

class BaseClass
{
    ...
    int amount()=0; // unimplemented method
    ...
};

In the above, BaseClass is an abstract class because the method amount() is declared as unimplemented (=0). A subclass must implement this method to become non-abstract.

Interfaces

Java has the keyword implements, which is a form of inheritance called interface inheritance. Whereas the regular extends inheritance actually brings in data and methods into a base class, an interface does not specify any actual capability, only the way it should be used. Java interfaces contain method declarations, but not any implementations; the class that inherits the interface must implement the methods.

Interfaces are abstract classes – indeed, they are purely abstract, in the sense of not have any implemented methods at all. In programming, you can still declare variables of interface types (since they are classes), and then you can use the interface methods, but the variable is always referring to an object of some class that implements the interface.

Can interfaces be created in C++? Yes! As we saw above, abstract classes can be created in C++ by having at least one unimplemented method – a body “=0”. So for an interface, all methods are “=0”! This forces the subclass to implement all of the methods in the interface, and then an object of the subclass can be used as an object of the interface class.

Remember, Java allows you to extend only one class; but you can implement many interfaces! So Java has single inheritance between classes, but multiple inheritance from interfaces. This is safe because interfaces have no actual behavior to inherit!

C++ does not distinguish between these kinds of inheritance – it is all captured in the classes you list after the inheritance colon (:). It is up to you to know if your base classes are real, abstract, or interfaces. Be careful!

Resources

Wikipedia:Inheritance is reasonable but spends too much effort at distinguishing inheritance from subtyping (also see Wikipedia:Subtyping). While there is some technical correctness in this, you should pretty much never use inheritance without purposeful subtyping. Long ago people thought “mix-in” inheritance was cool, but no longer. Be careful and purposeful using inheritance. Some authors even think Java extends is evil.