I got this code as an example of use of double dispatch, but I don't really understand one part of the code. creating the "abstract class" Printer, why I need to add:
virtual void print(PDFDoc *d)=0;
virtual void print(DocDoc *d)=0;
As I understand it, at run time p.print(docA); will send me to virtual void print(Document *d) of myPrinter, then d->printMe(this) will send me to printMe of PDFDoc and then it will call at run time to virtual void print(PDFDoc *d) of my printer?
So why defining
virtual void print(PDFDoc *d)=0;
virtual void print(DocDoc *d)=0;
is necessary for the abstract class?
class Document{
public:
//this is the accept function
virtual void printMe(Printer *p)=0;
};
class Printer{
public:
virtual void print(Document *d)=0;
//the visitors
virtual void print(PDFDoc *d)=0;
virtual void print(DocDoc *d)=0;
};
class PDFDoc : public virtual Document{
public:
virtual void printMe(Printer *p){
std::cout << "PDFDoc accepting a print call" << std::endl;
p->print(this);
}
};
class DocDoc : public virtual Document{
public:
virtual void printMe(Printer *p){
std::cout << "DocDoc accepting a print call" << std::endl;
p->print(this);
}
};
class MyPrinter : public virtual Printer{
public:
virtual void print(Document *d){
std::cout << "dispatching function <print> called" << std::endl;
d->printMe(this);
}
virtual void print(PDFDoc *d){
std::cout << "printing a PDF doc" << std::endl;
}
virtual void print(DocDoc *d){
std::cout << "printing a Doc doc" << std::endl;
}
};
int main(){
MyPrinter p;
Document *docA = new PDFDoc();
Document *docB = new DocDoc();
p.print(docA);
p.print(docB);
delete docA;
delete docB;
return 0;
}
Because the argument to
printMe()is the pointer to the abstract base class,Printer:And the purpose of the "double-dispatch" design pattern is to implement
print()passing the appropriate derivedDocumentclass as a parameter.Without the overloads for the derived
Documentclasses, the only method in the base class is the one that takes the abstractDocumentbase class:And without the additional overloads, this just calls the same virtual method that takes the virtual
Documentbase class as a parameter.The sequence of events is:
The virtual base class
Printergets called with the parameter being the virtual document classDocument.The actual printer implementations use on the actual class that's derived from Document.
So,
Document's pure virtualprintMe()method gets called, fromprint()that takes theDocumentpointer as a parameter.The only parameter that
printMe()takes is the virtualPrinterbase class pointer.So, whatever
printMe()calls, it can only call the methods defined in the virtualPrinterbase class.Therefore, if the actual printer implementation needs to use the derived
Documentclass, those methods have to be virtual methods in thePrinterbase class.Those virtual methods don't really have to be
print()overloads. They can be anything. It might be more clear, to some, to name them something different, likeprintPDF()andprintDoc(). If you were to rewrite them, as such, it might be clearer what's going on.