- In C++, dynamic dispatch does not consider the runtime type of paramters
- Double Dispatch: combination of overriding and overloading
- Want to add functionality that depends on runtime type of objects
- What if we don't have the source code?
- What if the behaviour does not really belong to the classes?
- We can do this if the Book hierarchy will "accept" BookVisitors
class Book { // Enemy
public:
virtual void accept(BookVisitor &v) { // strike(weapon)
v.visit(*this); // useOn
}
};
Class Text : public Book {
public:
void accept(BookVisitor &v) override { v.visit(*this); }
};
Class Comic : public Book {
public:
void accept(BookVisitor &v) override { v.visit(*this); } // this: Comic
};
class BookVisitor {
public:
virtual void visit(Book &) = 0;
virtual void visit(Comic &) = 0;
virtual void visit(Text &) = 0;
~BookVisitor();
};
- author -> # of Books
- topic -> # of Texts
- hero -> # of Comics
- string -> int
Template class, generalizes arrays
-
Parameterized on 2 types -> Key/Value
-
Key Type must support
operator<
-
std::map<string, int> m; m["abc"] = 5; m["def"] = 2; m.erase("abc"); m.count("abc"); for (auto p : m) { // std::pair<string, int> cout << p.first << p.second; } // Iteration is in sorted key order
Class Catalog : public BookVisitor {
map<string, int> cat;
public:
void visit(Book &b) override { ++cat[bgetAuthor()]; }
void visit(Comic &c) override { ++cat[c.getHero()]; }
};
se/visitor
:- cycle of includes
se/visitor2
:- Fix by forward declaring classes
- an include creates a dependency
- Best to reduce dependencies
- Avoid cycles
- Fewer recompilations
- Faster compile time
Advice: whenever possible, forward declare a class
class XYZ; // Hey compiler, this type will exist
// File "a.h"
class A {...};
// File "b.h"
#include "a.h"
class B : public A {
...
};
// File "c.h"
#include "a.h"
class C {
A a;
};
To construct C, you will need to know its size, and therefore need the size of A
// File "d.h"
class A;
class D {
A *pA;
};
// File "d.cc"
#include "a.h"
pA->method();
// File "e.h"
class A;
class E {
A foo(A a);
};
// File "e.cc"
#include "e.h"
#include "a.h"
A E::foo(A a) {}
// File "window.h"
#include <Xlib/Xlib.h>
class XWindow {
Display *d;
Window w;
GC gc;
public:
drawRect();
...;
};
// File "client.cc"
#include "window.h"
client.cc
needs to recompile even if a private member in window.h
changes
Strategy: pImpl Idiom (pointer to Implementation)**
// File "windowimpl.h"
#include <Xlib/Xlib.h>
struct WindowImpl {
Display *d;
Window w;
GC gc;
};
// File "window.h"
struct WindowImpl;
class XWindow {
WindowImpl *pImpl;
public:
...
};
// File "window.cc"
#include "window.h"
#include "windowimpl.h"
XWindow() : pImpl { new WindowImpl } {}
~XWindow() { delete pImpl; }
Extended the pImpl idiom to accomodate multiple implementation styles.