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



我正在尝试在c++中实现Oppen的算法。

该算法中的基本例程(打印和扫描)按令牌类型调度。使用访问者模式实现这种分派似乎很自然。问题是:例程是嵌套的,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方法无法访问该智能指针。

是否有一种方法可以直接在智能指针上实现访问者模式?如果没有,我如何减少复制文本令牌的成本?

技术上,您可以使用std::enable_shared_from_this来完成此操作。(注意皮特·柯克汉姆对这个问题的精彩评论——共享指针表明所有权。这适用于可能比其原始文档存在时间更长的访问者,例如,一个特别的字典构建器,它可能在文档关闭后存在。如果不涉及所有权,则使用原始指针。)

下面是代码的简化版本,说明了这一点:

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

#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 s选项:

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

所以具体的符号变成:

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_ptr s到token 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);
}

相关内容

  • 没有找到相关文章

最新更新