There is another solution: You can abstract from the Ellipse and Circle classes what they have in common and place those features in an ABC. Next, you derive both the Circle and Ellipse classes from the ABC. Then, for example, you can use an array of base-class pointers to manage a mixture of Ellipse and Circle objects—that is, you can use a polymorphic approach. In this case, what the two classes have in common are the coordinates of the center of the shape; a Move() method, which is the same for both; and an Area() method, which works differently for the two classes. Indeed, the Area() method can’t even be implemented for the ABC because it doesn’t have the necessary data members. C++ has a way to provide an unimplemented function by using a pure virtual function. A pure virtual function has = 0 at the end of its declaration, as shown for the Area() method:

class BaseEllipse  // abstract base class

{

private:

    double x;   // x-coordinate of center

    double y;   // y-coordinate of center

    ...

public:

    BaseEllipse(double x0 = 0, double y0 = 0) : x(x0),y(y0) {}

    virtual ~BaseEllipse() {}

    void Move(int nx, ny) { x = nx; y = ny; }

    virtual double Area() const = 0; // a pure virtual function

   ...

}

When a class declaration contains a pure virtual function, you can’t create an object of that class. The idea is that classes with pure virtual functions exist solely to serve as base classes. For a class to be a genuine ABC, it has to have at least one pure virtual function. It is the = 0 in the prototype that makes a virtual function a pure virtual function. In the case of the Area() method, the function has no definition, but C++ allows even a pure virtual function to have a definition. For example, perhaps all the base methods are like Move() in that they can be defined for the base class, but you still need to make the class abstract. You could then make the prototype virtual:

void Move(int nx, ny) = 0;

This makes the base class abstract. But then you could still provide a definition in the implementation file:

void BaseEllipse::Move(int nx, ny) { x = nx; y = ny; }

In short, the = 0 in the prototype indicates that the class is an abstract base class and that the class doesn’t necessarily have to define the function.

Now you can derive the Ellipse class and Circle class from the BaseEllipse class, adding the members needed to complete each class. One point to note is that the Circle class always represents circles, whereas the Ellipse class represents ellipses that can also be circles. However, an Ellipse class circle can be rescaled to a non-circle, whereas a Circle class circle must remain a circle.

A program using these classes would be able to create Ellipse objects and Circle objects but no BaseEllipse objects. Because Circle and Ellipse objects have the same base class, a collection of such objects can be managed with an array of BaseEllipse pointers. Classes such as Circle and Ellipse are sometimes termed concrete classes to indicate that you can create objects of those types.

In short, an ABC describes an interface that uses a least one pure virtual function, and classes derived from an ABC use regular virtual functions to implement the interface in terms of the properties of the particular derived class.

Applying the ABC Concept

You’d probably like to see a complete example of an ABC, so let’s apply the concept to representing the Brass and BrassPlus accounts, starting with an ABC called AcctABC. This class should contain all methods and data members that are common to both the Brass and the BrassPlus classes. The methods that are to work differently for the BrassPlus class than they do for the Brass class should be declared as virtual functions. At least one virtual function should be a pure virtual function in order to make the AcctABC class abstract.

Listing 13.11 is a header file that declares the AcctABC class (an ABC) and the Brass and BrassPlus classes (both concrete classes). To facilitate derived class access to base class data, AcctABC provides some protected methods. Recall that protected methods are methods that derived-class methods can call but that are not part of the public interface for derived-class objects. AcctABC also provides a protected member function to handle the formatting previously handled by nonmember functions. Also the AcctABC class has two pure virtual functions, so it is, indeed, an abstract class.

Listing 13.11. acctabc.h

// acctabc.h  -- bank account classes

#ifndef ACCTABC_H_

#define ACCTABC_H_

#include

#include

// Abstract Base Class

class AcctABC

{

private:

    std::string fullName;

    long acctNum;

    double balance;

protected:

    struct Formatting

    {

         std::ios_base::fmtflags flag;

         std::streamsize pr;

    };

    const std::string & FullName() const {return fullName;}

    long AcctNum() const {return acctNum;}

    Formatting SetFormat() const;

    void Restore(Formatting & f) const;

public:

    AcctABC(const std::string & s = "Nullbody", long an = -1,

                double bal = 0.0);

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги