2016-09-08 2 views
2

내 클래스의 모든 사용 사례를 잘 모르기 때문에 사용할 스마트 포인터 유형이 확실하지 않은 상황에 처해 있습니다. 공유 포인터를 사용할 수는 있지만 공유 소유권이 꼭 필요한 것은 아니지만 코드에서 공유 포인터를 도처에 전달할 생각이 들지 않습니다. This article Herb Sutter의 말에 의심 스러우면 unique_ptr을 사용하고해야 할 때 shared_ptr로 변환하십시오. 이것은 내가하고 싶은 것입니다 만,이 예제를 고려해이 작업을 수행 할 것으로 예상되는 방법으로 나는 분명 해요 :shared_ptr 및 unique_ptr 변환

class Example 
{ 
    public: 
     Example(): _ptr(std::make_unique<Node>()) {} 

     std::unique_ptr<Node>& getPtr() 
     { 
      return _ptr; 
     } 

    private: 
     // I am unsure if I will eventually need shared ownership or not 
     std::unique_ptr<Node> _ptr; 
}; 

Example* example = new Example(); 


// Some function somewhere 
void f() 
{ 
    // I've decided I need shared ownership, converting 
    std::shared_ptr<Node> ptr(std::move(example->getPtr())); 

    // Oops, example is no longer valid... 
} 

사람이 내가 같은 상황에 대처하는 방법의 더 나은 아이디어가있는 경우 그것을 듣게되어 기쁩니다. 다음과 같이

+2

unique_ptr 및 shared_ptr은 관련된 메모리의 OWNERSHIP에 대한 것이며 간단한 사용에는 해당되지 않습니다. 일반적으로 shared_ptr은 보통 포인터/참조와 비교하여 상대적으로 값이 비싸지 않으므로 전달하지 마십시오. – xaxxon

+0

'Example' *은 항상 노드에 대한 소유권을 유지할 필요가 있으며 때로는 다른 객체도 해당 노드의 소유권을 필요로합니까? – jaggedSpire

+1

위의 예에서 포인터가 소유권을 공유하고자 할 때 포인터가 'shared_ptr'이어야합니다. 공장에 대한 의심이 발생할 수 있습니다. – Jarod42

답변

1

나는 당신이 일종의 최적화 질문을하고 있다고 생각합니다.Exampleunique_ptr을 사용하려면 더 간단하고 효율적인 의미 체계가 있어야합니다 (참조 된 문서의 구문을 바꾸는 것). 그러나 필요가 생길 때 포인터를 shared_ptr으로 변환 할 수 있습니다.

Example은 인터페이스를 제공해야하며 사용자가 해당 인터페이스를 호출 할 때 unique_ptr에서 shared_ptr으로 변환해야합니다. 상태 패턴을 사용하여 인스턴스가 unique_ptr 모드 또는 shared_ptr 모드인지 여부를 캡처 할 수 있습니다.

class Example 
{ 
    struct StateUnique; 
    struct StateShared; 
    struct State { 
     State (std::unique_ptr<State> &s) : _state(s) {} 
     virtual ~State() = default; 
     virtual Node & getPtr() = 0; 
     virtual std::shared_ptr<Node> & getShared() = 0; 
     std::unique_ptr<State> &_state; 
    }; 
    struct StateUnique : State { 
     StateUnique (std::unique_ptr<State> &s) 
      : State(s), _ptr(std::make_unique<Node>()) {} 
     Node & getPtr() { return *_ptr.get(); } 
     std::shared_ptr<Node> & getShared() { 
      _state = std::make_unique<StateShared>(*this); 
      return _state->getShared(); 
     } 
     std::unique_ptr<Node> _ptr; 
    }; 
    struct StateShared : State { 
     StateShared (StateUnique &u) 
      : State(u._state), _ptr(std::move(u._ptr)) {} 
     Node & getPtr() { return *_ptr.get(); } 
     std::shared_ptr<Node> & getShared() { return _ptr; } 
     std::shared_ptr<Node> _ptr; 
    }; 
public: 
    Example(): _state(std::make_unique<StateUnique>(_state)) {} 
    Node & getNode() { return _state->getPtr(); } 
    std::shared_ptr<Node> & getShared() { return _state->getShared(); } 
private: 
    std::unique_ptr<State> _state; 
}; 

상태 머신 (이것은 설계를 통해-때문에이해야하는), 당신은 단지 두 Example에서 포인터, 그리고 그것을 사용할 필요가있는 하나의 테스트 할 필요가 당신의 방법을 유지할 수 무서운 보인다면

.

class Example 
{ 
public: 
    Example(): _u_node(std::make_unique<Node>()) {} 
    Node & getNode() { return _u_node ? *_u_node.get() : *_s_node.get(); } 
    std::shared_ptr<Node> & getShared() { 
     if (_u_node) _s_node = std::move(_u_node); 
     return _s_node; 
    } 
private: 
    std::unique_ptr<Node> _u_node; 
    std::shared_ptr<Node> _s_node; 
}; 
+0

이것이 본질적으로 내가하고 싶었던 것이지만,이 코드를 (이 클래스뿐만 아니라)이 패턴을 많이 사용하고 싶었습니다. 그것을 위해 너무 복잡합니다. 그러나 이것은 이것을하고자하는 사람들에게 옳은 대답입니다. – navark

+0

@navark : 답변의 맨 아래에 단순화 된 버전을 추가했습니다. – jxh

+0

왜 클래스에 unique_ptr과 shared_ptr을 둘 것입니까? 일반적인 포인터에 비해 shared_ptr의 유일한 증분 비용은 생성시이므로 앞뒤로 변환하지 않아도됩니다. 앞뒤로 변환하여 코드를 복잡하게 만들지 않아도됩니다. shared_ptr이 될 것이라면 shared_ptr을 만드십시오. 그리고 소유권을 이해한다는 관점에서 볼 때 unique_ptr과 같이 취급 할 수는 없지만 정적 분석 관점에서 shared_ptr 버전을 사용한다고 항상 가정해야합니다. 복잡한 장애 사례를 실제로 추가하는 것만으로도 아무런 이점이 없습니다. – xaxxon

1

당신은 클래스가있는 경우, UniqueResourceHolder 말, 그것은 구현 될 수있다 :

class UniqueResourceHolder{ 
public: 
    std::unique_ptr<Widget> ptr; 
}; 

나중에, 다음과 같이 UniqueResourceHolder에서 말했다 자원을 얻을 보이는 SharedResourceHolder에 넣어하려는 경우 :

class SharedResourceHolder{ 
public: 
    std::shared_ptr<Widget> ptr; 
}; 

과 같이 보일 수 있습니다 그렇게 할 수있는 코드 :

{ 
    UniqueResourceHolder urh; 
    SharedResourceHolder srh; 
    //initialization of urh.ptr 

    srh.ptr = std::shared_ptr<Widget>(urh.release()); 
} 

UniqueResourceHolder은 더 이상 Widget을 가지지 않으므로, urh을 사용하면 위젯에 들어가기위한 시도는 무효가됩니다 (새 것으로 다시 채우지 않는 한). srh은 이제 해당 위젯을 갖게되어 기꺼이 몫. 어쨌든, 그게 당신이 제공 한 링크의 질문에 대한 답을 이해하는 방법입니다. 내 자신의 두 센트는 std::unique_ptr의 발생을 std::shared_ptr으로 대체하는 것도 사소한 문제입니다. 리소스의 고유성을 보장하기 위해 프로그램이 수행 한 비즈니스 로직은 여전히 ​​유효하지만 공유 속성이 std::shared_ptr 인 비즈니스 로직에서 벗어나려면 고유 한 사고 방식으로 재 작업하는 데 시간과 노력이 필요합니다.

+0

간단히'srh.ptr = std :: move (urh.ptr);'을 할 수 있습니다. – Jarod42

+0

이것은 정확하게하고 싶지는 않지만 필요한 경우 unique_ptr을 shared_ptr로 바꾸는 것이 사소하고 최상의 솔루션이 될 수 있다고 생각합니다. – navark

0

공유 포인터는 공유 리소스 용입니다. 리소스를 공유하려면 처음부터 모든 것을 공유하고 특별한 전환을 거치지 말아야합니다.

고유 포인터를 사용하려면 할당 된 리소스에 대해 하나의 포인터 만 존재해야한다는 것을 의미합니다. 그렇다고해서 이러한 개체를 사용하는 방식이 제한되지 않습니다. 왜 고유 한 포인터를 사용하지 않고 클래스 인스턴스와 함수를 통해 객체를 "이동"하지 않을까요? 포인터로 변환해야하는 이유는 무엇입니까?

성능에 대해 : primite 포인터에 비해 공유 포인터는 약 6 배 느립니다. 고유 포인터는 약 두 배 느립니다. 물론 나는 접근에 대해 말하고있다. 코드의 성능에 중요한 부분이 있다면이 클래스의 원시 포인터를 얻을 수 있습니다. 포인터 일 필요조차 없습니다. C++ 참고 자료가 있습니다.

언제나 개체를 "공유"또는 "이동"할지 여부를 결정하고 모든 시간을 변환하고 변경하는 대신 패러다임을 계속 사용해야한다고 생각합니다.

std::unique_ptr<Node>& getPtr() 
{ 
    return _ptr; 
} 

아니요, 아니요. 다른 장소에서 호출하면 여러 참조를 전달합니다. 대신에 "move"연산자를 사용하고 (예 : type& operator=(type&& other)과 같은 선언문을 사용하여) 고유 한 포인터를 전달하십시오.

+0

내 이해가 있었기 때문에 고유 포인터를 사용하는 방법이 확실합니까? "고유성"이 소유권에 있었지만이를 참조하기 위해 원시 포인터 (또는 참조 된 unique_ptr)를 해당 unique_ptr에 전달할 수 있습니다. – navark

+0

확실하지 않습니까? "이동"은 "변경"이라고하지는 않지만 결국 포인터입니다. 객체/포인터가 프로그램을 통해 움직이는 것처럼 상상해 봅니다. 아마도 내 자신의 환상입니다. 원시 포인터를 전달하면 실제로 이러한 클래스의 이점을 사용하지 않고 "공유"합니다. 나는 원시 포인터를 얻는 것이 더 많은 선택이라고 생각한다. 왜냐하면이 포인터를 얻는 다른 방법이 있기 때문이다. 당신은 결국 템플릿 클래스를 사용하고 있습니다. –

+0

unique_ptr은 적어도 약간의 최적화가 가능한 한 원시 포인터보다 느리지 않습니다. – aschepler