In this section we redesign the num_sequence class of the preceding section in to an abstract base
class from which we inherit each of the numeric sequence classes. How do we go about that?
The first step in defining an abstract base class is to identify the set of operations common to its
children. For example, what are the operations common to all numeric sequence classes? These
operations represent the public interface of the num_sequence base class. Here is a first iteration:
elem() returns the element at the user-requested position. max_elems() returns the maximum
number of elements supported by our implementation. check_integrity() determines
whether pos is a valid position. print() displays the elements. gen_elems() generates the
elements for the sequence. what_am_i() returns a character string identifying the sequence. The next step in the design of an abstract base class is to identify which operations are type-dependent — that is, which operations require separate implementations based on the derived
class type. These operations become the virtual func tions of the class hierarchy. For example, each
numeric sequence class must provide a separate implementation of gen_elems() .
check_integrity(), on the other hand, is type-invariant. It must determine whether pos is a
valid element position. Its algorithm is independent of the numeric sequence. Similarly,
max_elems() is type-invariant. All the numeric sequences hold the same maximum number of
elements.
Not every function is this easy to distinguish. what_am_i() may or may not be type-dependent
depending on how we choose to implement our inheritance hierarchy. The same is true of elem()
and print(). For now, we'll presume that they are type-dependent. Later, we'll see an alternative
design that turns them into type-invariant functions. A static member function cannot be declared
as virtual.
The third step in designing an abstract base class is to identify the access level of each operation.
If the operation is to be available to the general program, we declare it as public . For example,
elem() , max_elems() , and what_am_i() are public operations.
If the operation is not meant to be invoked outside the base class, we declare it as private. A
private member of the base class cannot be accessed by the classes that inherit from the base class.
In this example, all the operations must be available to the inheriting classes, so we do not declare
any of them as private.
IA third access level, protected, identifies operations that are available to the inheriting classes
but not to the general program. check_integrity() and gen_elems() , for example, are
operations that the inheriting classes, but not the general program, must invoke. Here is our
revised num_sequence class definition:
Running Codes
class num_sequence {
public:
// elem(pos): return element at pos
// gen_elems(pos): generate the elements up to pos
// what_am_i() : identify the actual sequence
// print(os) : write the elements to os
//check_integrity(pos) : is pos a valid value?
// max_elems() : returns maximum position supported
int elem(int pos);
void gen_elems(int pos);
const char* what_am_i() const;
ostream& print(ostream &os = cout) const;
bool check_integrity(int pos);
static int max_elems();
// ...
};
class num_sequence {
public:
virtual ~num_sequence(){};
virtual int elem(int pos) const = 0;
virtual const char* what_am_i() const = 0;
static int max_elems(){ return _max_elems; }
virtual ostream& print(ostream &os = cout) const = 0;
protected:
virtual void gen_elems(int pos) const = 0;
bool check_integrity(int pos) const;
const static int _max_elems = 1024;
};
No comments:
Post a Comment