2016-09-29 2 views
2

Oppen's algorithm을 C++로 구현하려고합니다.C++ 스마트 포인터가있는 방문자 패턴

이 알고리즘 (인쇄 및 스캔)의 기본 루틴은 토큰 유형으로 발송됩니다. 방문자 패턴을 사용하여이 디스패치를 ​​구현하는 것이 자연스러운 것처럼 보입니다. 문제는 다음과 같습니다. 루틴이 중첩되어 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) /* .. */); 
    /* ... */ 
    } 
    /* .. */ 
} 

. 텍스트 토큰은 데이터 (std :: string)를 전달하므로 스트림으로 푸시 (push)되도록 복사해야합니다. 스트림은 Open 및 Text의 공통 추상 기본 클래스로 인해 포인터로 구성되어야합니다.

바깥쪽에는 텍스트 토큰에 대한 스마트 포인터가 있기 때문에 복사를 피하고 단순히 기존 스마트 포인터를 사용하고 싶습니다. 그러나 accept 메서드는 해당 스마트 포인터에 액세스 할 수 없습니다.

스마트 포인터에 직접 방문자 패턴을 구현할 수있는 방법이 있습니까? 그렇지 않은 경우 어떻게 텍스트 토큰을 복사하는 비용을 줄일 수 있습니까?

+0

'Visitor'의 당신의 정의는 무엇입니까? (또한 문제가 있음을 보여주기 위해 프로필을 작성하기 전에는 복사에 대해 너무 걱정하지 않아도됩니다.) –

+0

구현을 추가했습니다. 고전적인 방문자 패턴, AFAIK. 그리고 네, 최적화는 가치가 없을 수도 있지만, 그렇다면 제대로 수행하는 방법을 알지 못합니다. 그래서 묻습니다;) – choeger

+0

"바깥쪽에는 해당 텍스트 토큰에 대한 스마트 포인터가 있습니다."토큰의 각 *을 shared_ptr에 저장 하시겠습니까? 즉, (기본 클래스에) 공유 포인터의 범위가 있습니까, 당신은 그들 모두에게 방문자를 전달합니까? –

답변

5

기술적으로 std::enable_shared_from_this을 사용하면됩니다. (Pete Kirkham의 질문에 대한 탁월한 의견은 공유 포인터가 소유권을 나타냄을 나타냅니다. 이는 문서가 닫힌 후에도 살 수있는 임시 사전 작성기와 같이 원래 문서보다 수명이 오래가는 방문자에게 적용됩니다. 소유권이 관련되어 있으면 원시 포인터를 사용하는 것이 좋습니다.)

다음은이를 설명하는 코드의 단순화 된 버전입니다.

우리는 일반적인 방문자 패턴 전달 선언과 기본 클래스 정의로 시작한다고 가정 해 보겠습니다.이제 우리는 콘크리트 토큰을 할 때

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. 사용 우리가 visitor를 정의 할 때

    #include <memory> 
    #include <vector> 
    #include <iostream> 
    
    struct token; 
    
    struct visitor; 
    
    struct token { 
        virtual void accept(visitor &v) = 0; 
    }; 
    
    struct text_token; 
    struct open_token; 
    

    , 우리는 옵션이 acceptstd::shared_ptr들 수 있도록 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의 범위를 정의 할 수 있습니다
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을합니까? 'shared_ptrs'의 벡터에'uniqe_ptr'을 넣으시겠습니까? 또는 다른 방법으로'make_shared'를 사용하지 않을까요? (shared_ptr 컨트롤 블록은 보통 실제 객체와 동일한 메모리 할당으로 할당된다는 이점이 있습니다.) 그렇지 않으면 예 : This! –

+0

@MartinBonner 의견 주셔서 감사합니다 - 감사합니다! 정신 딸꾹질. 맞을 것이다. –