2017-02-10 25 views
1

Во-первых, извините, если это дубликат. Я не видел ничего подобного.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 
+0

Вы можете просто избавиться от визита (SubBNode & п) метод ваш класс посетителей? Похоже, тогда вы получите нужное поведение. –

+0

Правда, если у меня есть другой посетитель, который предназначен только для того, чтобы делать что-то с типами SubB, тогда вообще не удастся ответить на эти узлы. – extracrispy

ответ

1

Вы можете добавить дополнительный слой, что-то вроде:

template <typename F> 
class FunctorVisitor : public Visitor 
{ 
public: 
    explicit FunctorVisitor (F& f) : f(f) {} 

    virtual void visit(Node& n) override { f(n);} 
    virtual void visit(ANode& n) override { f(n);} 
    virtual void visit(BNode& n) override { f(n);} 
    virtual void visit(CNode& n) override { f(n);} 
    virtual void visit(SubBNode& n) override { f(n);} 
private: 
    F& f; 
}; 


class CountVisitor 
{ 
public: 
    void operator() (const Node& n) const { 
     cout << __PRETTY_FUNCTION__ << "\t\tDefault" << endl; 
    } 
    void operator() (const BNode& n) { 
     count++; 
     cout << __PRETTY_FUNCTION__ << "\t\tSPECIAL" << endl; 
    } 

    int count = 0; 
    void print() const { 
     cout << "CountVisitor Found Bs: "<< count << endl; 
    } 
}; 

Demo

+1

Конечно, хороший добрый функтор. Мне это нравится, спасибо. – extracrispy

 Смежные вопросы

  • Нет связанных вопросов^_^