C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 20:
[20.6] I have a heterogeneous list of objects, and my code needs to do class-specific things to the objects. Seems like this ought to use dynamic binding but can't figure it out. What should I do?

It's surprisingly easy.

Suppose there is a base class Vehicle with derived classes Car and Truck. The code traverses a list of Vehicle objects and does different things depending on the type of Vehicle. For example it might weigh the Truck objects (to make sure they're not carrying too heavy of a load) but it might do something different with a Car object — check the registration, for example.

The initial solution for this, at least with most people, is to use an if statement. E.g., "if the object is a Truck, do this, else if it is a Car, do that, else do a third thing":

typedef std::vector<Vehicle*>  VehicleList;

void myCode(VehicleList& v)
{
  for (VehicleList::iterator p = v.begin(); p != v.end(); ++p) {
    Vehicle& v = **p;  // just for shorthand

    // generic code that works for any vehicle...
    ...

    // perform the "foo-bar" operation.
    // note: the details of the "foo-bar" operation depend
    // on whether we're working with a car or a truck.
    if (v is a Car) {
      // car-specific code that does "foo-bar" on car v
      ...
    } else if (v is a Truck) {
      // truck-specific code that does "foo-bar" on truck v
      ...
    } else {
      // semi-generic code that does "foo-bar" on something else
      ...
    }

    // generic code that works for any vehicle...
    ...
  }
}
The problem with this is what I call "else-if-heimer's disease" (say it fast and you'll understand). The above code gives you else-if-heimer's disease because eventually you'll forget to add an else if when you add a new derived class, and you'll probably have a bug that won't be detected until run-time, or worse, when the product is in the field.

The solution is to use dynamic binding rather than dynamic typing. Instead of having (what I call) the live-code dead-data metaphor (where the code is alive and the car/truck objects are relatively dead), we move the code into the data. This is a slight variation of Bertrand Meyer's Law of Inversion.

The idea is simple: use the description of the code within the {...} blocks of each if (in this case it is "the foo-bar operation"; obviously your name will be different). Just pick up this descriptive name and use it as the name of a new virtual member function in the base class (in this case we'll add a fooBar() member function to class Vehicle).

class Vehicle {
public:
  // performs the "foo-bar" operation
  virtual void fooBar() = 0;
};
Then you remove the whole if...else if... block and replace it with a simple call to this virtual function:
typedef std::vector<Vehicle*>  VehicleList;

void myCode(VehicleList& v)
{
  for (VehicleList::iterator p = v.begin(); p != v.end(); ++p) {
    Vehicle& v = **p;  // just for shorthand

    // generic code that works for any vehicle...
    ...

    // perform the "foo-bar" operation.
    v.fooBar();

    // generic code that works for any vehicle...
    ...
  }
}
Finally you move the code that used to be in the {...} block of each if into the fooBar() member function of the appropriate derived class:
class Car : public Vehicle {
public:
  virtual void fooBar();
};

void Car::fooBar()
{
  // car-specific code that does "foo-bar" on 'this'
  ...   this is the code that was in {...} of if (v is a Car)
}

class Truck : public Vehicle {
public:
  virtual void fooBar();
};

void Truck::fooBar()
{
  // truck-specific code that does "foo-bar" on 'this'
  ...   this is the code that was in {...} of if (v is a Truck)
}
If you actually have an else block in the original myCode() function (see above for the "semi-generic code that does the 'foo-bar' operation on something other than a Car or Truck"), change Vehicle's fooBar() from pure virtual to plain virtual and move the code into that member function:
class Vehicle {
public:
  // performs the "foo-bar" operation
  virtual void fooBar();
};

void Vehicle::fooBar()
{
  // semi-generic code that does "foo-bar" on something else
  ...   this is the code that was in {...} of the else
  // you can think of this as "default" code...
}
That's it!

The point, of course, is that we try to avoid decision logic with decisions based on the kind-of derived class you're dealing with. In other words, you're trying to avoid if the object is a car do xyz, else if it's a truck do pqr, etc., because that leads to else-if-heimer's disease.