带有智能指针的C ++访问者模式

时间:2016-09-29 08:38:33

标签: c++ smart-pointers visitor-pattern

我正在尝试在C ++中实现Oppen's algorithm

此算法中的基本例程(打印和扫描)在令牌类型上进行调度。 使用访问者模式实现此调度似乎很自然。 问题是:例程是嵌套的,print()的参数在scan()期间在堆栈中排队。 为了避免任何内存问题,我想使用智能指针来完成任务。

所以我的实现看起来像这样:

class Text;
class Line;
class Open;
class Close;

class Visitor {
  /* Define virtual visit functions for concrete doc nodes:
   */
public:
  virtual void visit(const Text&) = 0;  
  virtual void visit(const Line&) = 0;  
  virtual void visit(const Open&) = 0;  
  virtual void visit(const Close&) = 0; 
};


class DocToken
{
protected:
  explicit DocToken() {}

  friend class Visitor;

public:
  virtual void accept(Visitor * visitor) const = 0;
};

class Text : public DocToken {
public:
  Text(std::string s) : text(s) {} 
  void accept(Visitor *visitor) const {
    visitor -> visit (*this);
  }
  std::string text;
};

class Open : public DocToken { /* .. */ }

/* .. */

class Scan : public Visitor {
  stream_t stream;
  /* ... */
public:
  void visit(const Open& x) {
    /* ... */ 
    stream.push_back(/* .. */ new Open() /* .. */);
    /* ... */ 
  }

  void visit(const Text& x) {
    /* ... */ 
    stream.push_back(/* .. */ new Text(x) /* .. */);
    /* ... */ 
  }
  /* .. */
}

如您所见,Open令牌不携带任何数据,可以轻松构建。 Text令牌确实携带数据(std :: string)并且必须被复制才能被推送到流中。 由于Open和Text的公共抽象基类,流需要由指针组成。

因为在外面,有一个指向该文本标记的智能指针,我想避免复制并简单地使用现有的智能指针。 但是,accept方法无法访问该智能指针。

有没有办法直接在智能指针上实现访问者模式?如果没有,我如何降低复制文本标记的成本?

1 个答案:

答案 0 :(得分:5)

从技术上讲,您可以使用std::enable_shared_from_this执行此操作。 (注意Pete Kirkham对这个问题的优秀评论 - 共享指针表明所有权。这适用于可能比他们的原始文档更长的访问者,例如,一个特殊的字典构建器,它可能在文档关闭后生存。涉及所有权,原始指针是要走的路。)

以下是您的代码的简化版本,说明了这一点。

假设我们从通常的访问者模式转发声明和基类定义开始。

#include <memory>
#include <vector>
#include <iostream>

struct token;

struct visitor;

struct token {
    virtual void accept(visitor &v) = 0;
};

struct text_token;
struct open_token;

当我们定义visitor时,我们会选择accept std::shared_ptr个选项:

struct visitor {
    virtual void accept(std::shared_ptr<text_token> p) = 0;
    virtual void accept(std::shared_ptr<open_token> p) = 0;
};

现在,当我们制作具体的代币时,我们:

  1. 子类std::enable_shared_from_this
  2. 使用shared_from_this将参数传递给accept
  3. 所以具体的代币变成了:

    struct text_token : public token, public std::enable_shared_from_this<text_token> {
        virtual void accept(visitor &v) override {
            std::shared_ptr<text_token> p{shared_from_this()};
            v.accept(p);
        }   
    };
    
    struct open_token : public token, public std::enable_shared_from_this<open_token> {
        virtual void accept(visitor &v) override {
            std::shared_ptr<open_token> p{shared_from_this()};
            v.accept(p);
        }   
    };
    

    具体访问者的变化不大:

    struct scan : public visitor {
        virtual void accept(std::shared_ptr<text_token>) override {
            std::cout << "accepting text" << std::endl;
        }
        virtual void accept(std::shared_ptr<open_token>) override {
            std::cout << "accepting open" << std::endl;
        }   
    };
    

    现在我们可以定义一系列std::shared_ptrtoken s

    int main() {
        std::vector<std::shared_ptr<token>> toks;
        toks.push_back(std::make_shared<text_token>());
        toks.push_back(std::make_shared<open_token>());
    

    并致电accept

        scan s;
        for(auto p: toks)
           p->accept(s);
    }
    

    运行时,会打印:

    $ ./a.out 
    accepting text
    accepting open
    

    完整代码

    #include <memory>
    #include <vector>
    #include <iostream>
    
    struct token;
    
    struct visitor;
    
    struct token {
        virtual void accept(visitor &v) = 0;
    };
    
    struct text_token;
    struct open_token;
    
    struct visitor {
        virtual void accept(std::shared_ptr<text_token> p) = 0;
        virtual void accept(std::shared_ptr<open_token> p) = 0;
    };
    
    struct text_token : public token, public std::enable_shared_from_this<text_token> {
        virtual void accept(visitor &v) override {
            std::shared_ptr<text_token> p{shared_from_this()};
            v.accept(p);
        }   
    };
    
    struct open_token : public token, public std::enable_shared_from_this<open_token> {
        virtual void accept(visitor &v) override {
            std::shared_ptr<open_token> p{shared_from_this()};
            v.accept(p);
        }   
    };
    
    struct scan : public visitor {
        virtual void accept(std::shared_ptr<text_token>) override {
            std::cout << "accepting text" << std::endl;
        }
        virtual void accept(std::shared_ptr<open_token>) override {
            std::cout << "accepting open" << std::endl;
        }   
    };
    
    int main() {
        std::vector<std::shared_ptr<token>> toks;
        toks.push_back(std::make_shared<text_token>());
        toks.push_back(std::make_shared<open_token>());
    
        scan s;
        for(auto p: toks)
           p->accept(s);
    }