FAQs in section [22]:
[22.1] What's the big deal of separating interface
from implementation?
Interfaces are a company's most valuable resources. Designing an interface
takes longer than whipping together a concrete class which fulfills that
interface. Furthermore interfaces require the time of more expensive people.
Since interfaces are so valuable, they should be protected from being tarnished
by data structures and other implementation artifacts. Thus you should
separate interface from implementation.
[ Top | Bottom | Previous section | Next section ]
[22.2] How do I separate interface from implementation
in C++ (like Modula-2)?
Use an ABC.
[ Top | Bottom | Previous section | Next section ]
[22.3] What is an ABC? 
[Recently renamed "subclass" to "derived class" (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.]
An abstract base class.
At the design level, an abstract base class (ABC) corresponds to an abstract
concept. If you asked a mechanic if he repaired vehicles, he'd probably wonder
what kind-of vehicle you had in mind. Chances are he doesn't repair
space shuttles, ocean liners, bicycles, or nuclear submarines. The problem is
that the term "vehicle" is an abstract concept (e.g., you can't build a
"vehicle" unless you know what kind of vehicle to build). In C++, class
Vehicle would be an ABC, with Bicycle, SpaceShuttle, etc, being
derived classes (an OceanLiner is-a-kind-of-a Vehicle). In real-world OO, ABCs
show up all over the place.
At the programming language level, an ABC is a class that has one or more
pure virtual member functions. You cannot make
an object (instance) of an ABC.
[ Top | Bottom | Previous section | Next section ]
[22.4] What is a "pure virtual" member function? 
[Recently renamed "subclass" to "derived class" (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.]
A member function declaration that turns a normal class into an abstract
class (i.e., an ABC). You normally only implement it in a derived class.
Some member functions exist in concept; they don't have any reasonable
definition. E.g., suppose I asked you to draw a Shape at location
(x,y) that has size 7. You'd ask me "what kind of shape should I
draw?" (circles, squares, hexagons, etc, are drawn differently). In C++, we
must indicate the existence of the draw() member function (so users can call
it when they have a Shape* or a Shape&), but we recognize it can
(logically) be defined only in derived classes:
class Shape {
public:
virtual void draw() const = 0; // = 0 means it is "pure virtual"
// ...
};
This pure virtual function makes Shape an ABC. If you want, you can think of
the "= 0;" syntax as if the code were at the NULL pointer. Thus
Shape promises a service to its users, yet Shape isn't able to provide any
code to fulfill that promise. This forces any actual object created
from a [concrete] class derived from Shape to have the indicated member
function, even though the base class doesn't have enough information to
actually define it yet.
Note that it is possible to provide a definition for a pure virtual function,
but this usually confuses novices and is best avoided until later.
[ Top | Bottom | Previous section | Next section ]
[22.5] How do you define a copy constructor or assignment
operator for a class that contains a pointer to a (abstract) base class?
If the class "owns" the object pointed to by the (abstract) base class pointer,
use the Virtual Constructor Idiom in the (abstract)
base class. As usual with this idiom, we declare a pure
virtual clone() method in the base class:
Then we implement this clone() method in each derived class:
class Circle : public Shape {
public:
// ...
virtual Shape* clone() const { return new Circle(*this); }
// ...
};
class Square : public Shape {
public:
// ...
virtual Shape* clone() const { return new Square(*this); }
// ...
};
Now suppose that each Fred object "has-a" Shape object. Naturally the
Fred object doesn't know whether the Shape is Circle or a Square or ...
Fred's copy constructor and assignment operator will invoke Shape's
clone() method to copy the object:
class Fred {
public:
Fred(Shape* p) : p_(p) { assert(p != NULL); } // p must not be NULL
~Fred() { delete p_; }
Fred(const Fred& f) : p_(f.p_->clone()) { }
Fred& operator= (const Fred& f)
{
if (this != &f) { // Check for self-assignment
Shape* p2 = f.p_->clone(); // Create the new one FIRST...
delete p_; // ...THEN delete the old one
p_ = p2;
}
return *this;
}
// ...
private:
Shape* p_;
};
[ Top | Bottom | Previous section | Next section ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Jul 10, 2000
|