The problem you describe could be solved using the Visitor pattern (everything can be solved using the Visitor pattern, so beware! )
The visitor pattern lets you move the implementation logic towards a new class. That way you do not need a base class, and a visitor works extremely well over different inheritance trees.
To sum up:
- New functionality does not need to be added to all different types
- The call to the visitor can be pulled up to the root of each class hierarchy
For a reference, see the Visitor pattern