Inheritance provides a mechanism to build new, improved, classes using already made classes. The fundamental principle goes like this: A new, derived class, keeps the methods and attributes from the old (base) class, and then gets some more attributes and more methods.
Inheritance makes it possible to build modular software. Complex software is often divided into small independent pieces called modules. Individual modules can then be used as building blocks in different software projects. You have probably written some functions only once. If you wrote them really well and made them easy to call, then you were able to use these same functions in other programs. Functions that sort sequences are good examples of functions that can be re-used in many programs.
It is often possible to insert exact same classes into different projects. For example, a class that implements fractions can be programmed only once. The same class can then be used in a variety of programs in number theory and numerical mathematics.
However, it is not often easy to design new programs in such a way that old classes can be used without any modifications. Inheritance is helpful when new classes need some additional features that the old classes did not have.
We will use one example to learn about inheritance. This example will teach us the procedures and syntax. The example will be very quick at achieving this goal. However, this example is not a good at teaching the art of designing software with inheritance. Building modular software is beyond the scope of these notes.
We will first create a class that models linear functions. We will call it LinearFunction
. Then we will create the class QuadraticFunction
using inheritance. We will be able to re-use several components of the class LinearFunction
.
Let us assume that we figured out how to make a class that stores coefficients of linear functions of the type \(f(x)=bx+a\) and makes the evaluations of the function for a diverse set of input values \(x\). The following code has the implementation of the class and a main function that uses the class.
#include<iostream> class LinearFunction{ private: double a; double b; public: void set_a(double ); void set_b(double ); double get_a() const; double get_b() const; double evaluate(double ) const; }; void LinearFunction::set_a(double x){ a = x;} void LinearFunction::set_b(double x){ b = x;} double LinearFunction::get_a() const{return a;} double LinearFunction::get_b() const{return b;} double LinearFunction::evaluate(double x) const{ return b*x+a; } int main(){ LinearFunction lf; lf.set_a(5); lf.set_b(9); std::cout<<lf.evaluate(10)<<"\n"; return 0; }
The quadratic function has the form \(g(x)=cx^2+bx+a\), where \(c\), \(b\), and \(a\) are real numbers. We will now build the class that inherits from LinearFunction
. The inheritance is triggered with the syntax
class QuadraticFunction : public LinearFunction{ // ... };
We will explain the word public
later in section 6.
LinearFunction
is called base class or parent class. QuadraticFunction
is called derived class or child class.The class QuadraticFunction
now has all the attributes and all the methods that LinearFunction
has. It does not have a direct access to the attributes a
and b
because they were private
in LinearFunction
. However, the methods get_a
, get_b
, set_a
, and set_b
are public
. These methods provide all the access we need.
We need to add another attribute c
and the methods get_c
and set_c
. We will also override the method evaluate
. When evaluate
is called on an object of the class QuadraticEquation
a different formula will be used.
#include<iostream> class LinearFunction{ private: double a; double b; public: void set_a(double ); void set_b(double ); double get_a() const; double get_b() const; double evaluate(double ) const; }; void LinearFunction::set_a(double x){ a = x;} void LinearFunction::set_b(double x){ b = x;} double LinearFunction::get_a() const{return a;} double LinearFunction::get_b() const{return b;} double LinearFunction::evaluate(double x) const{ return b*x+a; } class QuadraticFunction : public LinearFunction{ private: double c; public: void set_c(double ); double get_c() const; double evaluate(double ) const; }; void QuadraticFunction::set_c(double x){ c = x;} double QuadraticFunction::get_c() const{return c;} double QuadraticFunction::evaluate(double x) const{ return c*x*x+get_b()*x+get_a(); } int main(){ QuadraticFunction qf; qf.set_a(5); qf.set_b(9); qf.set_c(2); std::cout<<qf.evaluate(10)<<"\n"; return 0; }
The pointer of type LinearFunction*
can be used to store the address of an object of QuadraticFunction
. Later, you will see examples where this feature is very useful. However, we will first see some troubles that need to be corrected.
Let us assume that the classes LinearFunction
and QuadraticFunction
are defined as above and let us consider the following main()
function.
int main(){ QuadraticFunction qf; LinearFunction* aQ; qf.set_a(5); qf.set_b(9); qf.set_c(2); aQ=&qf; std::cout<<aQ->evaluate(10)<<"\n"; return 0; }
The pointer aQ
is of type LinearFunction*
. Although it is declared as a pointer to the base class, aQ
is allowed to contain an address of an object of the derived class. The assignment aQ=&qf;
would correctly assign the address of qf
to the variable aQ
.
However, the evaluation aQ->evaluate(10)
causes trouble. It calls the wrong method. It calls the method from the base class instead of the method from the derived class.
The method evaluate
in the derived class is an override of the method from the base class. The base class must contain the word virtual
in front of the name of the method. Then it will be able to route the function calls correctly when the methods are accessed from pointers. We will now list the improved code.
#include<iostream> class LinearFunction{ private: double a; double b; public: void set_a(double ); void set_b(double ); double get_a() const; double get_b() const; virtual double evaluate(double ) const; }; void LinearFunction::set_a(double x){ a = x;} void LinearFunction::set_b(double x){ b = x;} double LinearFunction::get_a() const{return a;} double LinearFunction::get_b() const{return b;} double LinearFunction::evaluate(double x) const{ return b*x+a; } class QuadraticFunction : public LinearFunction{ private: double c; public: void set_c(double ); double get_c() const; double evaluate(double ) const; }; void QuadraticFunction::set_c(double x){ c = x;} double QuadraticFunction::get_c() const{return c;} double QuadraticFunction::evaluate(double x) const{ return c*x*x+get_b()*x+get_a(); } int main(){ QuadraticFunction qf; LinearFunction* aQ=&qf; qf.set_a(5); qf.set_b(9); qf.set_c(2); std::cout<<aQ->evaluate(10)<<"\n"; return 0; }
#include<iostream> class A{ public: virtual long fun1() const; long fun2() const; }; long A::fun1() const{return 1;} long A::fun2() const{return 2;} class B : public A{ public: long fun1() const; virtual long fun2() const; }; long B::fun1() const{return 3;} long B::fun2() const{return 4;} int main(){ A* a=new B; B* b=new B; long result = 1000*a->fun1()+ 100*a->fun2(); result+=10*b->fun1()+b->fun2(); std::cout<<result<<"\n"; delete a; delete b; return 0; }
1
?
#include<iostream> class A{ public: virtual long fun(long = 0) const; }; long A::fun(long x) const{return 1;} class B : public A{ public: long fun(double = 0) const; }; long B::fun(double x) const{return 2;} int main(){ B b; A* p=&b; std::cout<<p->fun()<<"\n"; return 0; }
The pointers to base class cannot be used to access all of the methods from the derived class. They can only access the overriden methods, and only if they are virtual. Assume that we have previously defined classes LinearFunction
and QuadraticFunction
. Assume that the object qf
and the pointer aQ
are defined with:
QuadraticFunction qf; LinearFunction* aQ=&qf;
We are not allowed to give the command aQ->set_c(2);
. The pointer aQ
if of type LinearFunction*
. It can only see the methods of the class LinearFunction
. However, if aQ->evaluate
is called, then aQ
will realize that this is a virtual method, and it will search for the implementation outside of LinearFunction
.
If the class requires the implementation of the destructor, and if there is a possibility that the class could be a base in an inheritance, then the destructor should be virtual.
The declaration would look like this:
class A{ // ??? public: virtual ~A(); };
We had a minor inconvenience while implementing QuadraticFunction
. We were not able to access the coefficients a
and b
directly. The coefficients a
and b
are private members of LinearFunction
. Only the methods of LinearFunction
are allowed to access these private members.
It is possible to make an exception. We can allow the methods of derived classes to access the attributes a
and b
, and still prohibit everybody else from accessing a
and b
. To achieve such outcome, we need to use the word protected
instead of private
.
The modified code in which we use the keyword protected is listed below.
#include<iostream> class LinearFunction{ protected: double a; double b; public: void set_a(double ); void set_b(double ); double get_a() const; double get_b() const; virtual double evaluate(double ) const; }; void LinearFunction::set_a(double x){ a = x;} void LinearFunction::set_b(double x){ b = x;} double LinearFunction::get_a() const{return a;} double LinearFunction::get_b() const{return b;} double LinearFunction::evaluate(double x) const{ return b*x+a; } class QuadraticFunction : public LinearFunction{ private: double c; public: void set_c(double ); double get_c() const; double evaluate(double ) const; }; void QuadraticFunction::set_c(double x){ c = x;} double QuadraticFunction::get_c() const{return c;} double QuadraticFunction::evaluate(double x) const{ return c*x*x+b*x+a; }
The method QuadraticFunction::evaluate
can now use the variables a
and b
. Before we had to use get_a()
and get_b()
instead.
We constructed QuadraticFunction
using public inheritance from LinearFunction
. There are two more types of inheritance: private
and protected
. The type of inheritance changes how the methods of the base class will be accessed by the objects and descendants of the derived class.
Let us look at the following example of the base class, and three classes derived from the base class.
class Base{ private: int a; protected: int b; public: int c; }; class DerivedA : private Base{ /* ... */}; class DerivedB : protected Base{ /* ... */}; class DerivedC : public Base{ /* ... */};
The attribute a
is private in Base
. This attribute is not accessible from any of the classes DerivedA
, DerivedB
, and DerivedC
.
The attribute b
is protected in Base
. It will be accessible from each of DerivedA
, DerivedB
, and DerivedC
. It will be assumed to have status protected
in each of the classes DerivedB
and DerivedC
. However, it will become private
in class DerivedA
.
The attribute c
is public in Base
. It will remain public in DerivedC
. It will turn into protected
in DerivedB
. It will turn into private
in DerivedA
.
The types of inheritance are summarized in the table below. Each row follows the destiny of a member of the base class. The 0-th row lists the types of inheritance. The row 1 covers the destiny of a private member of the base class. The row 2 follows a protected member of the base class. The row 3 follows a public member of the base class.
\begin{eqnarray*} \begin{array}{|c||c|c|c|}\hline \mbox{member}\setminus \mbox{inheritance} & \mbox{private} & \mbox{protected} & \mbox{public}\\ \hline \hline \mbox{private} & \mbox{not accessible} &\mbox{not accessible} & \mbox{not accessible}\\ \hline \mbox{protected} & \mbox{private} & \mbox{protected} & \mbox{protected}\\ \hline \mbox{public} & \mbox{private} & \mbox{protected} & \mbox{public}\\ \hline \end{array} \end{eqnarray*}The following link from stackoverflow has a discussion with multiple ways of summarizing the types of inheritance: https://stackoverflow.com/questions/860339/what-is-the-difference-between-public-private-and-protected-inheritance-in-c.
The keyword protected
should be avoided as much as possible. It breaks the encapsulation. When you are designing a class you had your reasons for guarding the private members. You did not want to expose them to the outside world. The private
section is amazing at guarding the vulnerable pieces of your code.
The protected
section does not actually offer much of a protection. Anyone can now access them. All they need to do is to pretend to be nice people, make a derived class, and use that derived class to access the protected members.
There is a problem though, it is very tempting to convert private
to protected
in late stages of the code. The world of code is full of protected
members. Almost every programmer at some point decided to save time and allowed the keyword protected
to find its way into the code.
The first stage of the program consists of a user providing different functions through the user input. The user repeatedly chooses one of the options 1, 2, 4, and 5. When the user chooses 5, this is the end of the function input phase of the program.
In the second phase the user inputs a positive integer \(n\) and the sequence of \(n\) lines of text. Each line contains the name of the function and the argument of the function. The program should evaluate the functions at the given arguments, add up the results of all evaluations and print the result on the standard output.
Example:
Input: 4 0.5 0.0 1.0 apple 2 0.01 0.01 grape 4 0.01 0.0 0.5 watermelon 1 7.0 -2.0 mouse 1 -2.0 -5.0 dog 5 3 grape 2.0 watermelon 5.0 mouse 5.0
Output:
53.1481