CSCI 2300 | Data Structures and Algorithms | Spring 2000 |
In CS II you were introduced to the notion of a class hierarchy, where new object types are created from existing object types through inheritance. Classes may be organized into a logical, tree-like structure according to functionality, with the most general classes at the top of the tree. Common illustrative examples include class hierarchies of bank account types, geometric figures, and zoo animals. Within such a hierarchy the classes will have common functionality, but often different implementations. For example, associated with a geometric figure is the notion of an area and perhaps a volume. The way these are calculated depends on the type of figure; there are different ways of calculating area for a circle, a rectangle, and a sphere. Therefore each figure class has its own mechanism for calculating area. On the other hand, we might want to create a list containing a variety of different figure objects , and we might want to know the areas of each of the objects or sort them by area. Doing this requires sending a message (making a function call) to each object and having it compute its area. This is done correctly through the use of virtual functions. The list of different figures is a polymorphic list.
To explore the notions of virtual functions and polymorphism, work through the following steps, in order:
Next examine carefully the constructor for square. The first step in any constructor, before executing the function body (between the "{" "}"), is a call to the base class constructor, if there is a base class. If not specified explicitly, as shown here, this call is done implicitly. (It can't be prevented!) In this case, an implicit call would be cause a compiler error because there is no "default constructor" for rectangle. Try it (delete the explicit call) and see!
Also, compare the constructors for circle and sphere. Each accomplishes the same effect - initialization of member variables - but in subtly different ways. The sphere constructor does this before the body of the constructor. This is the preferred method, both in terms of style and in terms of efficiency.
The main program is simple, just dynamically allocating a few objects. It will be discussed in more detail below.
This isn't the desired solution, however. If we need to keep a list of different objects, we can't be identifying them with different pointers. Instead, we have to slightly change the class hierarchy. In particular, we need to declare the Output_Info, Area and Volume functions as virtual. For now, do this only for the Output_Info function in figure.h. In the declaration of the base class Figure precede the declaration of Output_Info with the keyword virtual:
virtual void Output_Info( );This makes the Output_Info function in all derived classes virtual as well.
Note: stylistically, it is better to explicitly name the functions in the derived class as virtual, even though this is redundant.If a function is virtual then the actual function called is determined at run-time (i.e. when the program is executed) according to the type of objected pointed to. By contrast when a function is non-virtual, the actual function called is determined at compile time (when the program is compiled) according to the pointer type.
virtual void Output_Info( ) = 0;Do this in figure.h and remove the corresponding function from figure.C. Then, recompile. There will be errors in the main program that you need to fix by deleting lines.
Note: derived classes will be concrete (as opposed to abstract) when all pure virtual functions are redefined. Only objects of concrete classes can be created.
Note: the implementation might have been simpler if we had made Cylinder a derived class of Circle. This isn't a great idea, however, because it runs counter to the notion of inheritance: a cylinder is not a specialization of a circle; rather a cylinder has a circle as part of its physical structure. The distinction between "is-a" and "has-a" is the primary criteria used in choosing between making class A a derived class of B and making A use an object of type B as a member variable.
Examine this file, mainvect.cpp. You will notice the use of the standard library container class vector and its member functions (push_back), along with "iterators" and generic functions such as sort. A vector is in essence a dynamically resized array. It contains all the functionality of arrays without having to fix the size in advance. The namespace prefix std:: is required as part of C++ standard.
We will explore standard library container classes in more detail in labs 4 and 5. You should have also seen them in CS II. They were (and still are) previously described as being part of STL, the "Standard Template Library". They are now in the C++ standard and may simply be thought of as being part of the standard library.
The standard library (STL) also includes generic functions such as sort. These functions work with iterators, so they may be used with a variety of container classes (including plain old arrays!). The sort function takes either two or three arguments. The first two are the beginning and ending iterators. The last is the comparison function. If this function is not specified the overload operator< operator is used on the type stored. Since the type stored in this case is a pointer and since we can't overload this operator on a pointer, we must provide a function explicitly.
At the conclusion of this lab, take a moment to review the following:
You should have recalled the notions of class hierarchies and inheritance.
You have been introduced to and explored virtual functions and polymorphism. Think about both the intuitions as to why this is important and the mechanisms that make it work. The latter may seem tricky until you have more experience, and are often a source of errors in programs.
You have been introduced to the sometimes subtle distinction between "is-a" and "has-a" - the choice between gaining access to a class through inheritance or though a member variable. As a general rule, inheritance ("is-a") should be reserved for specialization.
You have been (re-)introduced to the vector class from the standard library and the notions of iterators and generic functions. These are covered in more detail in recent C++ text books and in the optional text STL Tutorial and Reference Guide: C++ Programming with the Standard Template Library. These will be covered in more detail in labs 4 and 5.