访客模式如何编写包含内容? [简单的例子]

时间:2019-04-22 22:16:09

标签: c++ visitor visitor-pattern

我似乎无法通过这个简单的例子弄清楚如何编写“访客模式”的包含内容。不管我做什么,我总是以循环依赖而告终,但是没有其他方法有意义。

我也为不同的头文件保护(pragma vs. #ifndef)表示歉意,我正在测试#pragma,并且尚未更新文件。

Client.cpp

#include "OneVisitor.h"
#include "DataStructure.h"

int main (int argc, char * argv [])
{
    OneVisitor v;
    DataStructure d;

}

DataStructure.h

#ifndef _DATA_STRUCTURE_H_
#define _DATA_STRUCTURE_H_

#include "ElementA.h"

class DataStructure {

    public:
        DataStructure (Visitor & v)
        {   
            std::cout << "ACCEPTS";
            a->accept(v);
        };

    private:
        ElementA * a;

};

#endif

Element.h

#ifndef _ELEMENT_H_
#define _ELEMENT_H_

#include "Visitor.h"
#include <iostream>

class Element {

    public:
        virtual void accept (Visitor & v) = 0;

        void talk ()
        {
            std::cout << "ELEMENT TALKING";
        };

};

#endif

ElementA.h

#pragma once

#include "Element.h"
#include "Visitor.h"

class ElementA : public Element {

    public:
        virtual void accept (Visitor & v) override
        {
            v.Visit(*this);
        };

        void talk ()
        {
            std::cout << "ELEMENT A TALKING";
        };

};

Visitor.h

#ifndef _VISITOR_H_
#define _VISITOR_H_

#include "ElementA.h"

class Visitor {

    public:
        virtual void Visit (ElementA & a) = 0;

};

#endif

OneVisitor.h

#ifndef _ONE_VISITOR_H_
#define _ONE_VISITOR_H_

#include "Visitor.h"

class OneVisitor : public Visitor {

    public:
        virtual void Visit (ElementA & a) override
        {
            a.talk();
        };
};

#endif

运行此命令时,在Element.h,ElementA.h,ElementB.h中收到错误“未声明访问者”。如何在这些类中定义Visitor而不引起循环依赖?

2 个答案:

答案 0 :(得分:2)

访问者是一个非常抽象的概念,在这种情况下,将其模板化是有意义的。使用模板可以使我们摆脱循环依赖,并大大简化事情。

eventClick: function (event) {
    // Prevent redirect to Google Calendar
    event.jsEvent.cancelBubble = true;
    event.jsEvent.preventDefault();
}

现在,如果您想吸引// Visitor.hpp #pragma once template<class T> class Visitor { public: virtual void visit(T& item) = 0; virtual ~Visitor() = default; }; 的访问者,则可以使用Element

Visitor<Element>

现在我们有了这些东西,我们还可以编写一个函数将lambda转换为访客:

// Element.hpp
#pragma once
#include "Visitor.hpp"
#include <iostream>

class Element
{
   public:
    virtual void accept(Visitor<Element>& v) 
    {
        v.visit(*this); 
    }
    virtual void talk() {
        std::cout << "Element talking!\n"; 
    }
    virtual ~Element() = default; 
};

将所有内容放在一起

这使我们可以编写像这样的漂亮代码:

template<class T, class Func>
struct FunctionVisitor : public Visitor<T> {
    Func func;
    FunctionVisitor() = default;
    FunctionVisitor(FunctionVisitor const&) = default;
    FunctionVisitor(FunctionVisitor&&) = default;

    FunctionVisitor(Func const& func) 
      : func(func) 
    {
    }
    void visit(T& item) override {
        func(item); 
    }
};

template<class T, class Func>
FunctionVisitor<T, Func> makeVisitor(Func const& f) {
    return FunctionVisitor<T, Func>(f); 
}

答案 1 :(得分:2)

通过在 Visitor.h

中使用class ElementA;的前向声明
#ifndef _VISITOR_H_
#define _VISITOR_H_

// Just use a forward declaration of the class ElementA;
// NOTE1: The include of ElementA.h is not needed anymore.
// NOTE2: The visitor.h doesn't need to know what is defined
// in ElementA, only your .cpp needs, this is how forward 
// declaration works.
class ElementA;

class Visitor {

    public:
        virtual void Visit (ElementA & a) = 0;

};

#endif