Inheritance- V


The following topics are covered in this section:


Relationships between Classes (Object composition and private inheritance):

Let’s take the following scenario:

· We’ve modeled an engine as a class.

· We’ve modeled a wheel as another class.

· Engine and wheel are not related to each other (that’s quite obvious).

Now we want to model a class called car. Let’s say our first requirement is that a car has an engine. We already have an engine class but how should we use it within the class car? We might be tempted to do the following:

class engine
{//engine class specifications
};

class car: public engine
{//define this class
};

Now we have established a relationship between the car and the engine. Next we need to relate wheel with car. After all, a car has wheels. So we might be tempted to do the following:

class car: public engine, public wheel
{
//define this class
};

That seems good. “But wait a minute”, you say. “A car has 4 wheels. How do we represent that in this relationship?” That’s a very good observation and leads us to the fact that our modeling has a major flaw. The way we’ve created the relation between car and engine was wrong in the first place.

Why? Let’s go back to the beginning of this chapter where we said: “A Ferrari is a car”. Then we proceeded to derive Ferrari from a car because a Ferrari is a highly specialized version of a car. Or another example is the class ‘employee’ and ‘accountant’. An accountant is an employee (a specialized employee trained in accounting). The key words are “is a”.

If we coded:

class accountant: public employee

then it is equivalent to the statement “An accountant is an employee”.

So when we coded:

class car: public engine

this actually means that “a car is an engine!”.

Voila! That’s the mistake; a car isn’t an engine. A car has an engine. Similarly a car has wheels (a car isn’t a wheel).

Sometimes we might be able to get the desired functionality in the wrong way. If our car didn’t have wheels then

class car: public engine

might still be sufficient for us. After all, a car has only one engine. But again, the means of achieving the result is wrong and it can lead to disastrous side effects. It would also mislead anyone who reads your code later (they would assume that a car is an engine).

So, in this scenario public inheritance won’t work. The ‘has a’ relationship is called composition. A car is composed of an engine, 4 wheels, a steering wheel etc. Or in other words, a car has an engine; a car has 4 wheels; a car has a steering wheel and so on.

In C++ this “has a” relationship can be expressed using object containment (or object composition). It’s quite simple:

class car
{
private:
    wheel bridgestone[4];
    engine honda;
};

Well, wasn’t that easy! We are able to model the real world car just as we desired.

Note: The relationship between classes (‘is a’, ‘has a’ etc.) are not something specific to C++. They are part of the Object Oriented principles. So if you happen to read any books/ tutorials on object oriented programming you are bound come across these terms.

In object oriented terminology, we have different forms of composition. They are called composition, association and aggregation. They all have subtle variations but as far as C++ is concerned you needn’t worry about them. The main idea is the concept of whole and part. In our example the car is the whole entity and the parts are the engine, wheel etc. The whole entity is composed of many parts.

Let’s take an example to illustrate object composition:

class engine
{
private:
//status denotes whether engine is ON or OFF
//width specifies one dimension of the engine
    int status;
    int width;

public:
engine(int wd=5)
{
        status=0;
        width=wd;
        cout<<endl<<"Engine created with width:"<<width;
}

~engine( )
{
        cout<<endl<<"Engine destroyed";
}

void start( )
{
        cout<<endl<<"Starting the engine";
        status=1;
}

};

class car
{
private:
    engine honda;
    int speed;

public:
car( ):honda(20)
{}

~car( )
{
        cout<<endl<<"Car destroyed";
}

void start( )
{
        cout<<endl<<"Starting the car";
        honda.start( );
}

};

int main( )
{
car mycar;
mycar.start( );
return 0;
}

The output is:

Engine created with width:20 Starting the car
Starting the engine
Car destroyed
Engine destroyed

A few points to note in the above example:

1.)

car( ):honda(20)
{}

To initialize the engine object honda, we’ve made use of an initializer list in the class car. This is one use of an initializer list (the same cannot be implemented in a constructor body).

2.) The constructor and destructor of the class engine are invoked automatically upon creating a car object.

3.) The engine object honda cannot be directly accessed by the user of the class car (because it is lying inside the private region of car). Of course the user can create a stand-alone engine but cannot manipulate the engine present in a car.

In our example, we want to provide an interface for starting the car (not for starting the engine). If we permitted that (by making engine honda public), then the user can start the engine and then start the car (which is not what we want). Thus:

mycar.honda.start( ); //is now illegal
mycar.start( ); //is the only way to start the car (and engine)

If you remember, there were 3 relationships listed in the beginning of this chapter:

1. is a

2. has a

3. is implemented in terms of

The first 2 relations should be clear to you by now. We’ve encountered the following statement a couple of times already:

· Every Ferrari is a car (but not every car is a Ferrari).

Some similar statements would be:

· Every teacher is an employee (but not every employee need be a teacher).

· Every student is a person (but not every person is a student)

The words “is a” are important in these statements. When we establish an “is a” relationship between 2 objects it means that we should inherit from the base class using the public access specifier.

Ex:

class employee
{
//class definition
};

class teacher: public employee
{
//class definition
};

According to the above relationship, whatever an employee can do a teacher can also do (that is the basis of an ‘is-a’ relationship). Or as far as the programming language is concerned, a function which takes an employee as argument can take a teacher as argument (but a function taking a teacher as argument can’t take an employee as argument). Every teacher is an employee but not every employee is a teacher. Thus public inheritance helps incorporate the ‘is-a’ relationship in C++.

We also know that instead of public inheritance, we can inherit another class as private or protected (called private inheritance and protected inheritance).

So, what does the following mean?

class employee
{
//class definition
};

class teacher: private employee
{
//class definition
};

Going back to our example about an engine and a car, we decided not to use public inheritance because it gave the meaning: a car is an engine. But what about private/protected inheritance?

The following examples illustrate some of the difference between public and private inheritance:

1.) A room isn’t a door.

class door
{};

class room:private door
{};

void common_func(door d)
{}

int main( )
{
door d1;
room r1;
common_func(d1);
common_func(r1);
return 0;
}

If the derived class were inherited publicly then the function call:

common_func(r1);

will work fine because of the “is a” relationship. But in our program, the compiler complains:

conversion from 'class room *' to 'const class door &' exists, but is inaccessible

When we use private inheritance, everything in the base class becomes private in the derived class and so the conversion from derived to base isn’t possible.

In an “is a” relationship, any function which operates on the base class can operate on the derived class (or in other words whatever a car can do, a Ferrari can also do). But in private inheritance this doesn’t hold true.

2.) Another difference is that the public members of the base class now become private in the derived class. Now a user cannot open the door twice!

class door
{
public:
    void open( )
    {
        cout<<endl<<"Door opened";
    }
};

class room:public door             //Public inheritance
{
public:
    void open( )
    {
        door::open( );
        cout<<endl<<"Room opened";
    }
};

int main( )
{
room r1;
r1.open( );
r1.door::open( );         //permitted. Not an error
return 0;
}

The output is:

Door opened
Room opened
Door opened

Actually, when the room is opened it implies that the door has been opened. And this is why the function open( ) in the class ‘room’ has been defined as:

void open( )
{
door::open( );
cout<<endl<<"Room opened";
}

Internally the open( ) function of class ‘door’ is called.

But with public inheritance the user isn’t prevented from doing the following:

r1.open( );
r1.door::open( ); //permitted. Not an error

The user opened the room (which in turn led to opening of the door) and then the user again opened the door explicitly. But we wouldn’t want this to happen. If private inheritance were used, this would cause a compile-time error; since door::open( ) would then be a private function and private functions cannot be directly accessed by objects.

This leads us to the thought that private inheritance is similar to object composition. Some of their features are:

· In both cases we’ll have only one instance of the other object (i.e. a room will have only one door and a car will have only one engine).

· But in object composition we can create a room with multiple doors (this is not possible in private inheritance)

· In both cases we can prevent the user from peforming actions such as ‘opening the door twice, starting the engine twice etc’.

· In both cases we don’t establish the “is-a” relationship.

There is another significant difference between private inheritance and object composition. In private inheritance the derived class can access all the protected members of the base class.

Object Composition (ERROR) Private Inheritance (permitted)
class door
{
protected:
void shut( )
  {
    cout<<endl<<"Door closed";
  }
};

class room
{
private:
   door d1;
public:
   void close( )
   {
     cout<<endl<<"Closing the room"; 
     d1.shut( );
   }
};
class door
{
protected:
   void shut( )
   {
    cout<<endl<<"Door closed";
   }
};

class room:private door
{
public: void close( )
  {
    cout<<endl<<"Closing the room";
    door::shut( );
   }
};

When we inherit a class privately, the protected members (data and functions) of the base class become private in the derived class. The public functions of the derived class can invoke these functions (which were inherited from the base class).

But in the case of object composition, this is not possible. The class ‘room’, can only access the public parts of class ‘door’.

The private inheritance relationship is termed “is implemented in terms of”. Protected inheritance is similar to private inheritance except that another derived class will inherit the protected members of this class.

Ex:

class base
{};

class derived1:protected base
{};

class derived2:private derived1
{};

The class ‘derived1’ can access the protected members of the class ‘base’. The class ‘derived2’ can access the protected members of class derived1. But any class derived from ‘derived2’ will not be able to access the protected members inherited from ‘derived1’ (because derived2 privately inherited derived1).


Reinforcing the OOP concepts:

Let’s just take a quick look at the various OOP concepts in brief.

Whenever you are faced with a problem (to develop an application) you should first identify the problem. The necessary details have to be separated out from the unnecessary details. The abstract model (which is a class in C++) has to be modeled based on the relevant details. Let us suppose that someone puts forth the following requirement:

“I want to have a program to keep track of all the cars I own. I’m interested in knowing the colour and top speed of the car…”

In real life cars have many properties but in this particular problem we are only concerned with two properties: top speed and colour. These are the relevant details (relevant for our requirement) which are required to construct an abstract model. For some other problem it may be necessary to include other properties like type of fuel, dimensions etc. Abstraction is a way of looking at the same real-life entity from different perspectives. Depending on the requirements we create our own abstract view of the problem.

Encapsulation means hiding implementation details while providing a consistent interface to the user. In other words, no matter how you modify the implementation your interface should remain the same (i.e. the user will be unaffected by changes in the implementation). It’s similar to our telephone connection. The interface is the socket where we simply plug in the telephone jack and the implementation is the media carrying our voice to the telephone exchange (the implementation is hidden from us. As users we wouldn’t know if the media were changed from copper cables to fibre optic cables. Such changes do not affect the user since the user will still need to simply plug the jack into the telephone socket).

Inheritance is the process of establishing relationships between classes. It helps in reuse of code rather than rewriting the same code. The 3 major relationships are:

  • Is-a

  • Is-implemented-in-terms-of

  • Has-a

  • A fourth relation termed ‘a-kind-of’ is basically an ‘is-a’ relationship. If you really want to distinguish between the two, we could say that ‘is-a-kind-of’ relationship is at the class level while ‘is-a’ relationship is at the object level.

    In C++ we have the option of deciding how we want to inherit the parent (as public, private or protected). When a relationship is of the type ‘is-a’, we will inherit the parent class using the ‘public’ access specifier. In this case the relationship should be such that whatever action can be performed on the base class is permissible on the subclass also.

    In other cases you will have to inherit either as ‘private’ or ‘protected’ (depending on whether the current subclass will be further used as a parent class or not) - this represents the ‘is-implemented-in-terms-of’ relationship.

    Object composition represents the ‘has-a’ relationship.


    Recap


    Go back to the Contents Page 2


    Copyright © 2004 Sethu Subramanian All rights reserved.