Здесь функции ловушки сами должны проверять typeid члена данных, который также принадлежит иерархии классов. Поэтому я определяю шаблонный метод для этой иерархии классов. Это беспорядок, с которым я сталкиваюсь:
void Person::leave() {
// code
hook(); // private virtual
// code
}
void Girl::hook() { // Girl is a derived class of Person, with data member location
// of type Location which itself has a derived class House
// code
location.locationHook(this);// what happens here depends on what kind of location she is in
// code
}
void Location::locationHook(Person* person) {
// Oh oh! This depends on what class person is
}
void House::locationHook(Person* person) {
// Oh oh! This depends on what class person is
}
Поэтому в этой ситуации я должен прибегнуть к своему первоначальному методу использования typeid (* person) и dynamic_cast и операторов if для каждого типа производного класса Person для определения виртуального locationHook’s, верно?
Это решение, которое я придумал. Я проверил, что это работает, но я не знаю, получит ли оно какое-либо одобрение или нет (или оно всегда будет работать):
void Person::leave() {
// code
hook();
}
void Person::hook() {
// code
location.locationHook (this);
}
void Girl::hook() {
// code
location.locationHook (this);
}
void Location::locationHook (Person* person) {
// code
person->removeFromLocation();
}
void House::locationHook (Person* person) {
// code
person->removeFromHouse();
}
// removeFromLocation() and removeFromHouse() are also virtual functions of Person
Вот альтернативное решение с использованием двойной отправки:
#include <iostream>
#include <list>
struct Person; struct Girl; struct Guy;
struct Location {
virtual void hook (Person*) = 0;
virtual void hook (Girl*) = 0;
virtual void hook (Guy*) = 0;
};
struct House : Location {
virtual void hook (Person*) override;
virtual void hook (Girl*) override {
std::cout << "Girl left the house.\n";
}
virtual void hook (Guy*) override {
std::cout << "Guy left the house.\n";
}
};
struct Person {
Location* location = new House;
void leave() {
// ...
hook();
// ...
}
virtual void invoke_hook (Location*) = 0;
private:
virtual void hook() = 0;
};
struct Girl : Person {
virtual void hook() override {
std::cout << "Girl will leave the house.\n";
location->hook(this);
// ...
}
virtual void invoke_hook (Location* location) override { location->hook(this); } // Double dispatch
};
struct Guy : Person {
virtual void hook() override {
std::cout << "Guy will leave the house.\n";
location->hook(this);
// ...
}
virtual void invoke_hook (Location* location) override { location->hook(this); } // Double dispatch
};
void House::hook (Person* person) {
person->invoke_hook(this); // Double dispatch
}
int main() {
std::list<Person*> people = {new Girl, new Guy};
for (Person* p : people)
p->leave(); // Simple overloads will do the job here.
House* house = new House;
for (Person* p : people)
house->hook(p); // Double dispatch used.
}
Выход:
Girl will leave the house.
Girl left the house.
Guy will leave the house.
Guy left the house.
Girl left the house.
Guy left the house.