Во-первых, извините, если это дубликат. Я не видел ничего подобного.C++ Visitor Pattern с посетителем над подклассами узлов теряет отношение «есть»
Я знаком с шаблоном посетителя, и я пытаюсь добавить немного большую гибкость для посетителя на моем графике. Если у меня есть узловые классы A, B, C, SubB (наследует B), я бы хотел иметь посетителя, который принимает узлы B, и будет автоматически принимать узлы SubB, не зная о них (определяя accept()).
Очевидным преимуществом является то, что я могу подклассифицировать весь день, и посетителю не нужно заботиться об определении посещения для этих типов.
У меня есть код и результаты, чтобы продемонстрировать ниже. Как вы можете видеть, вот узлы, которые он посетит.
new Node(), new ANode(), new BNode(), new CNode(), new BNode(), new SubBNode()
В основном CountVisitor правильно сообщает, что нашел 2 Bs, и я понимаю, почему. CountVisitor.visit (SubB &) не переопределяется и будет перенаправляться на Visitor.visit (SubB &), тем самым пропуская счет. Тем не менее, мне хотелось бы, чтобы функциональность там, где он сообщал 3 (2 Bs + 1 SubB), потому что SubB «является« B. Я подумал об этом, и я просто не могу понять, как заставить систему типов сделать это. Как мне изменить порядок действий для достижения этой функциональности? Я также открыт для альтернативных шаблонов, если это поддерживает отношения «есть», но я думаю, что посетитель был бы идеальным, если бы эта проблема была решена.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Visitor;
class Node
{
public:
vector<Node*> children;
virtual void accept(Visitor&);
};
class ANode : public Node
{
public:
virtual void accept(Visitor&);
};
class BNode : public Node
{
public:
virtual void accept(Visitor&);
};
class CNode : public Node
{
public:
virtual void accept(Visitor&);
};
//-- try inheritance
class SubBNode: public BNode
{
public:
virtual void accept(Visitor&);
};
//--
class Visitor
{
public:
virtual void visit(Node& n);
virtual void visit(ANode& n);
virtual void visit(BNode& n);
virtual void visit(CNode& n);
virtual void visit(SubBNode& n);
};
class CountVisitor : public Visitor
{
public:
virtual void visit(BNode& n);
int count = 0;
void print();
};
//---------------------------------------------
void Node::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void ANode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void BNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void CNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void SubBNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
// -----
void Visitor::visit(Node& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(ANode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(BNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(CNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(SubBNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
// -----
void CountVisitor::visit(BNode& n){
count++;
cout << __PRETTY_FUNCTION__ << "\t\tSPECIAL" << endl;
}
void CountVisitor::print(){
cout << "CountVisitor Found Bs: "<< count << endl;
}
// ====================================================
int main() {
cout << "======FLAT TEST======" << endl;
vector<Node*> nodes = {
new Node(),
new ANode(),
new BNode(),
new CNode(),
new BNode(),
new SubBNode()
};
cout << "--DEFAULT--" << endl;
Visitor v1;
for(Node* n : nodes){
n->accept(v1);
}
cout << "--COUNT--" << endl;
CountVisitor cv1;
for(Node* n : nodes){
n->accept(cv1);
}
cv1.print();
return 0;
}
РЕЗУЛЬТАТЫ
======FLAT TEST======
--DEFAULT--
virtual void Node::accept(Visitor&)
virtual void Visitor::visit(Node&) DEFAULT
virtual void ANode::accept(Visitor&)
virtual void Visitor::visit(ANode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void Visitor::visit(BNode&) DEFAULT
virtual void CNode::accept(Visitor&)
virtual void Visitor::visit(CNode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void Visitor::visit(BNode&) DEFAULT
virtual void SubBNode::accept(Visitor&)
virtual void Visitor::visit(SubBNode&) DEFAULT
--COUNT--
virtual void Node::accept(Visitor&)
virtual void Visitor::visit(Node&) DEFAULT
virtual void ANode::accept(Visitor&)
virtual void Visitor::visit(ANode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void CountVisitor::visit(BNode&) SPECIAL
virtual void CNode::accept(Visitor&)
virtual void Visitor::visit(CNode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void CountVisitor::visit(BNode&) SPECIAL
virtual void SubBNode::accept(Visitor&)
virtual void Visitor::visit(SubBNode&) DEFAULT
CountVisitor Found Bs: 2
Вы можете просто избавиться от визита (SubBNode & п) метод ваш класс посетителей? Похоже, тогда вы получите нужное поведение. –
Правда, если у меня есть другой посетитель, который предназначен только для того, чтобы делать что-то с типами SubB, тогда вообще не удастся ответить на эти узлы. – extracrispy