For implementing double dispatch. Virtual methods are chosen based on the run-time type of an object on which they’re called. What if you want to choose a method based on two objects?
Example: Striking enemies with weapons
We want something like
. But, that is not possible.
If we write virtual void Enemy::strike(weapon &w), then the method is chosen based on the enemy type but not the weapon type.
If we write virtual void weapon::strike(Enemy &e), then we choose based on weapon type but not enemy type
The trick to getting double dispatch is to combine overloading and overriding.
Virtual dispatch for beStruckBy occurs, calling (Turtle or Bullet)::beStruckBy
The appropriate overwritten method calls w.strike on itself, the argument reference type is then a Bullet or a Turtle based on which method was chosen above
Then, the appropriate strike method is chosen based on virtual dispatch.
Our stick or our rock, hits the turtle or bullet appropriately.
However, this design pattern has issues:
We want re-usability, abstraction, and extendability
Visitor pattern can be used to add functionality to existing classes without changing or recompiling them, so long as they offer the the “visit” interface.
Example: Adding a visitor to the Book hierarchy
Now, let’s write an Application of our BookVisitorclass.
e.g. How many of each type of Book, we have:
group Books by author
group Texts by topic
group Comics by hero
So, the Book and Catalogue code above wont compile. Why? book.h includes Bookvisitor.h, which includes text.h, text.h includes book.h - a circular inclusion.
Because of the header guard, text.h doesn’t actually get a copy of book.h, so, the compiler doesn’t know what a Book is when we define Text as a subclass.
Our text.h absoultely needs Book.h. So, this raises the question, are all of these \#includes necessary?
Compilation Dependencies
When does a compilation dependency exist, i.e., when does a file REALLY need to include another?
Consider:
In general, do not introduce compilation dependencies where they don’t actually exist. Forward declare when possible, and include only when necessary.
Now, the implementation files will likely have true dependencies, but that’s OK.
Consider the Xwindow class:
What if we add, or change, a private member? If so, all client code must recompile. This seems unnecessary. The clients shouldn’t care about private.