Programming Workshop 2 (CSCI 1061U)
Faculty of Science, Ontario Tech University
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:
(string name, int age) { ... }
Person
friend ostream& operator<<(ostream& os, const Person& p) { ... }
;
string nameint 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:
(string name, int age, int grade);
Student
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:
(string name, int age, string subject, float salary);
Teacher
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
* a = new Student(...); Person
This has important implications, which will come to later.
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
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.
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 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() {
<< x << endl; // Compiler error, since x is a private member of
cout // base class A
(); // Compiler error, since boo is a private member
boo// of base class A
}
}
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 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() {
<< x << endl; // Ok, since x is a protected member of
cout // base class A
(); // Ok, since boo is a private member
boo// of base class A
}
}
int main()
{
;
A a.boo() // Compiler error, since boo is a
a// protected member of class A.
<< a.x << endl; // Compiler error, since x is a protected
cout // of class A.
}
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
.
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.