First | Next | Previous | Last | Glossary | About |
One of the most useful features of classes is inheritance. You might want to review the concept here. It is possible to declare a class which inherits the properties of another class or classes. This means that, with good class design, you can build applications which are based on proven re-usable code. Strictly speaking that's true of structured programming too but classes do extend the reusability of code, particularly complex code.
An abstract class is a base class. It is called abstract because it is an abstraction of the classes that will descend from it. Did that explanation confuse you? If it did here is an example. I am going to use buildings as a model for the work on inheritance. You know any building can be characterised by a number of features:
and so on. Every building has at least these characteristics but a particular building will have specific characteristics. A particular building might have a flat roof, two floors and be made of concrete. A house will differ from an office block but both buildings will share the same basic characteristics. The abstract class is representative of all buildings but is not itself a particular building. We don't normally directly access the abstract class, it will be used by the derived classes.
As a first step I define some data types and constants. We will use these, and add to them, right through the lessons on inheritance.
// STEP 1 #include <iostream> #define MINFLOORS 1 typedef enum rooftype { FLAT, PITCHED, SKILLION }; typedef enum constructiontype { STONE, CLAYBRICK, STEEL, WOODEN, CONCRETE, MUDBRICK}; const char *RoofDesc[] = {"flat", "pitched", "skillion"}; const char *ConstructionDesc[] = {"stone", "claybrick", "steel", "wooden", "concrete", "mudbrick" };
The enumerated types help us to avoid having to use numbers to specify different values.
The const chars are the display strings we use with the enums. For example:
rooftype r; ... r = FLAT; ... cout << RoofDesc[r];
The second step is to declare our abstract building type.
// STEP 2 class building { public: building(); void build(rooftype, constructiontype, int); void inspect(); protected: rooftype roof; constructiontype walls; int floors; private: };
You can see the form doesn't differ from other classes you have declared. It has the simplest of constructors, apart from no constructor at all, and two public member functions. It also has three protected data members and no private members.
The role of the protected members in abstract classes is very important. Do you remember reading this?
It was early in the lessons on classes and might have puzzled you at the time. The data members of the abstract class building need to be made available to the classes which inherit from building, consequently they go in the protected section of the class. It doesn't make sense to use a protected section if the class isn't a base class.
Now, when we construct a descendent of building, we can access the data members.
The third step is to define our abstract building type.
// STEP 3 building::building() { cout << "A new building" << endl; } void building::build(rooftype r, constructiontype w, int f) { roof = r; walls = w; if (f > 0) floors = f; else floors = MINFLOORS; }; void building::inspect() { cout << RoofDesc[roof] << "," << ConstructionDesc[walls] << "," << floors << " floors." << endl; }
Defining the class involves writing the code for the constructor and the member functions.
In the constructor I have a cout statement. Of course this isn't required but it helps, while we are learning about this, to see the constructor being called
The build() method sets the characteristics of the building. All buildings which inherit this class should have at least these characteristics.
Last the inspect() method displays the characteristics of the building.
Although building is an abstract class we can still use it like a normal class. After all that's what it is. Later, as we develop this further, we won't directly refer to the building class, all references will use derived classes. It is rare to actually directly use the base class.
// STEP 4 int main() { building home, skyscraper; home.build(SKILLION, CLAYBRICK, 1); skyscraper.build(FLAT, CONCRETE, 120); home.inspect(); skyscraper.inspect(); return 1; }
The variable declaration building home, skyscraper calls the constructor so you should see two messages:
A new building A new building
when you run the program. It gives you an indication that the objects home and skyscraper have been created.
Next we call the build() method for the objects and then the inspect() method.
Here is some tutorial work for you.
1.1 Save the code section labelled // STEP 1 in a file called declares.h
1.2 Save the code sections labelled // STEP 2, // STEP 3 and // STEP 4 in a file called bldex1.cc and add the statement:
#include "declares.h"
to the file.
1.3 Build the program and run it.
1.4 Add a third building called office, give it some features and inspect it.
We are now ready to use inheritance.
// STEP 5 #include <iostream> #include "declares.h" class building { ... }; building::building() { ... } void building::build(rooftype r, constructiontype w, int f) { ... }; void building::inspect() { ... };
The earlier declarations and definitions for the building class are unchanged except for the addition of the "declares.h" statement.
Now we declare a descendent class.
class domestic : public building { public: domestic(); }; domestic::domestic() { cout << "Domestic building" << endl; }
To inherit the base class we add the new class, in this case domestic, and say (in a Pascalish sort of way) that it derives from the class building by adding : public building.
This is a nearly minimum descendent, all it has is a constructor but it has inherited all the function members and data members of building because we have said, in effect, that domestic is of type building.
Now we should make some use of the new class.
int main() { domestic emoh_ruo; emoh_ruo.build(PITCHED, MUDBRICK, 1); emoh_ruo.inspect(); return 1; }
The object emoh_ruo is declared as type domestic, the descendent class. emoh_ruo doesn't have build and inspect methods of its own so it uses the inherited methods.
When you run the program you will see some messages on the screen:
A new building Domestic building pitched,mudbrick,1 floors.
The first indicates that the building constructor was called, the second indicates that the domestic constructor was called and the last is an inspection of the new building.
Here is some tutorial work for you.
2.1 Take a copy of your work from Tutorial 1. and add the new class to it then build and run the program to verify that it works.
2.2 Add second derived class called commercial. Keep it basic just like the domestic class and test your program.
The two new classes are very minimal classes since they don't do anything that the abstract class doesn't do and don't contain any data members that aren't already in the abstract class.
//STEP 8 #include <iostream> #define MINFLOORS 1 typedef enum rooftype {FLAT, PITCHED, SKILLION}; typedef enum constructiontype {STONE, CLAYBRICK, STEEL, WOODEN, CONCRETE, MUDBRICK}; typedef enum commercialtype {OFFICE, FACTORY, SHOP}; const char *RoofDesc[] = {"flat", "pitched", "skillion"}; const char *ConstructionDesc[] = {"stone", "claybrick", "steel", "wooden", "concrete", "mudbrick" }; const char *CommercialDesc[] = { "office", "factory", "shop", "garage"};
We'll change that in the next few steps. But first we make some additions to "declares.h".
There is nothing you haven't seen before, I have just added another enum and a string array for the commercial class you introduced in the previous tutorial.
These changes should be made to "declares.h" before you make the changes that come up in the following steps.
Now we make the changes to the domestic and commercial building classes.
class building { public: building(); building(rooftype, constructiontype, int); void inspect(); protected: rooftype roof; constructiontype walls; int floors; private: }; class commercial : public building { public: commercial(); commercial( rooftype, constructiontype, int, commercialtype); void inspect(); private: commercialtype usage; }; class domestic : public building { public: domestic(); domestic( rooftype, constructiontype, int, int, int, int); void inspect(); private: int allrooms, bedrooms, bathrooms; };
You can see that the building class has been modified. It has an additional constructor. Like the new domestic and commercial classes it has a default constructor and a constructor with arguments.
You can also see that all the classes have two constructors, their own inspect() function members and private data members.
Do we need two constructors in each of the classes?
We don't but it will prove useful later. Even now it might be useful if we wanted to create an instance of domestic or of commercial but without giving them any specific properties.
The classes have been declared so now for the definitions.
building::building() { cout << "Building." << endl; } building::building(rooftype r, constructiontype w, int f) { roof = r; walls = w; if (f > 0) floors = f; else floors = MINFLOORS; } void building::inspect() { cout << RoofDesc[roof] << "," << ConstructionDesc[walls] << "," << floors << " floors, "; } commercial::commercial() { } commercial::commercial(rooftype r, constructiontype w, int f, commercialtype u ) { roof = r; walls = w; floors = f; usage = u; } void commercial::inspect() { cout << RoofDesc[roof] << "," << ConstructionDesc[walls] << "," << floors << " floors, "; cout << CommercialDesc[usage] << endl; } domestic::domestic() { } void domestic::inspect() { building::inspect(); ght think I am labouring the point but keep in mind that protected data members can only be accessed by descendents. It doesn't make sense to have protected data members if a class is not going to be inherited.Why not just put the data members in the public section of the parent class?
It would defeat the point of data hiding.
We can remove the inspect() method of building since it isn't needed now we don't directly access the building class. You can see in the commercial::inspect() that no call is made to the building::inspect() method. It was left in domestic::inspect() so that you can compare them.
Getting started - 11
And last of all - using the classes.
int main() { commercial skyscraper(FLAT, CONCRETE, 120, OFFICE); domestic home(SKILLION, MUDBRICK, 1, 8, 3, 1); domestic estate[10]; home.inspect(); skyscraper.inspect(); return 1; }You can see that there is no direct reference to the abstract class. The only objects we can see are a skyscraper, a home and an estate of ten houses of unknown construction.
The objects are created and we inspect two of them.
Tutorial 3
Here is some tutorial work for you.
3.1 Take a copy of of the new program and build it to make sure it runs.
3.2 Now completely remove the building::inspect() member function and modify the domestic::inspect() accordingly.
3.2 A building has other characteristics apart from those we have used. Add the following data members and modify the program to use them:
First | Next | Previous | Last | Glossary | About |
Copyright © 1999 - 2001
David Beech