2016-09-29 18 views
2

Oppen's algorithmをC++で実装しようとしています。C++ Visitorパターンとスマートポインタ

このアルゴリズム(印刷とスキャン)の基本ルーチンは、トークンタイプでディスパッチします。 訪問者パターンを使用してこのディスパッチを実装するのは当然のようです。 問題は、ルーチンがネストされており、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) /* .. */); 
    /* ... */ 
    } 
    /* .. */ 
} 

あなたが見ることができるように、オープン・トークンは、任意のデータを運ばないと、容易な場所に構築することができます。 Textトークンはデータ(std :: string)を持ち、ストリームにプッシュするためにコピーする必要があります。 ストリームは、OpenおよびTextという共通の抽象基本クラスのためにポインタで構成する必要があります。

外部には、そのテキストトークンへのスマートポインタがあるので、私はコピーを避け、単に既存のスマートポインタを使用したいと思います。 ただし、acceptメソッドはそのスマートポインタにアクセスできません。

スマートポインタにビジターパターンを直接実装する方法はありますか?そうでない場合、どのようにしてテキストトークンのコピーコストを削減できますか?

+0

'Visitor'のあなたの定義は何ですか? (また、あなたがそれが問題であることを示すためにプロファイリングするまで、私はコピーについてあまり心配しないでしょう)。 –

+0

私は実装を追加しました。古典的なビジターパターン、AFAIK。そして、ええ、最適化はそれに値するものではないかもしれませんが、そうであれば、それを正しく行う方法はまだ分かりません。だから私は尋ねます;) – choeger

+0

"外側には、そのテキストトークンへのスマートなポインタがあります。" *トークンの各*を 'shared_ptr'に保存していますか?つまり、(基本クラスへの)共有ポインタの範囲がありますか?あなたはそれらのすべてに訪問者を渡しますか? –

答えて

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を定義するとき、我々はそれのオプションのacceptstd::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は、引数をに渡します。 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; 
    } 
}; 

今、私たちはtoken

std::shared_ptr 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); 
} 
+0

'main'関数ではなぜpush_back 'uniqe_ptr'を' shared_ptrs'のベクトルに置きますか?別の言い方をすると、なぜ 'make_shared'を使わないのでしょうか? (shared_ptrコントロールブロックは通常、実際のオブジェクトと同じメモリ割り当てで割り当てられるという利点があります)。そうでなければ、はい:これ! –

+0

@MartinBonnerコメントありがとう!ありがとう!精神的なしゃっくり。訂正します。 –

関連する問題