Collection of derived classes with extended interfaces. How to access derived interface without dynamic cast?-Collection of common programming errors

The visitor pattern is a viable solution. The solution has two main participants:

  • Elements: The distinct types with a common parent that will accept a Visitor. In this case, the Elements are Cat and Dog with the common parent being Animal.
  • Visitor: The class that will visit Elements, and can invoke Element specific operations as it has a handle to the specific Element type.

For this example, begin with the elements (Animal, Cat, and Dog):

class Animal
{
public:
  virtual ~Animal() {}
  virtual void eat() = 0;
};

class Cat: public Animal
{
public:
  void destroyFurniture();
  void eat(); 
};

class Dog: public Animal
{
public:
  void chaseTail();
  void eat();
};

Next, create a Visitor that will ‘visit’ each Element. The Visitor will know the type it is operating on, so it can use methods on both the specific Elements, such as Cat::destroyFurniture() and Dog::chaseTail():

class Visitor
{
public:
   void visitDog( Dog& dog ) { dog.chaseTail();        }
   void visitCat( Cat& cat ) { cat.destroyFurniture(); }
};

Now, add a pure virtual method to Animal that accepts a Visitor as an argument: void Animal::accept( Vistor& ). The idea is to pass a Visitor to an Animal, and allow the virtual method to resolve to the specific runtime type. Once the virtual call is resolved, the implementation can invoke the specific visit method on the Visitor.

class Animal
{
public:
  ...
  virtual void accept( Visitor& ) = 0;
};

class Cat: public Animal
{
public:
  ...
  virtual void accept( Visitor& visitor ) { visitor.visitCat( *this ); }
};

Notice how the virtual method is used to resolve to the specific Element type, and that that each element’s accept implementation will invoke a method on the Visitor. This allows for execution to branch based on type without the use of dynamic_cast, and is commonly referred to as double dispatch.

Here is a compilable example that demonstrates the pattern in use:

#include 
#include 

using std::cout;
using std::endl;

class Cat;
class Dog;

class Visitor
{
public:
   void visitCat( Cat& cat );
   void visitDog( Dog& dog );
};

class Animal
{
public:
  virtual ~Animal() {}
  virtual void eat() = 0;
  virtual void accept( Visitor& ) = 0;
};

class Cat: public Animal
{
public:
  void destroyFurniture()         { cout