2013-11-14 2 views
4

내 프로그램에서 디자인 문제가 있습니다. 루트 ChainDescriptor의 일부인 Nodes 객체를 관리해야합니다.C++ : 원시 포인터를 공유 및 약한 ptr로 대체하십시오.

은 기본적으로는 다음과 같습니다

class ChainDescriptor 
{ 
public: 
    ~ChainDescriptor() 
    { 
     //delete the nodes in nodes... 
    } 

    void addNode(Node *); 
    Node * getNode(); 

    const std::list<Node *>& getNodes() const; 

    std::list<Node *> m_nodes; 

}; 

class Node 
{ 
public: 
    Node(Node *parent); 

    void addChild(Node *node); 
    Node * getChild(const std::string& nodeName); 

private: 
    Node * m_parent; 
    std::list<Node*> m_childs; 
}; 

ChainDescriptor 클래스는 모든 노드를 소유하고 삭제의 책임이있다. 그러나 이러한 클래스는 이제 다른 프로그램, 즉 "소유권"문제가있는 실행 취소/다시 실행 기능이있는 GUI에서 사용해야합니다. 깊이 기존 코드를 수정하기 전에, 내가 고려하고 다른 솔루션 :

  • weak_ptr 위의 예에서

각각 list<weak_ptr<...> >를 사용 shared_ptr 및 각각의 list<shared_ptr<...> >

  • 를 사용하여, 나는 '돈 shared_ptrweak_ptr을 올바르게 사용할 위치를 실제로 알지 못합니다.

    의견이 있으십니까?

  • +0

    ['boost :: ptr_list'] (http://www.boost.org/doc/libs)는 어떨까요? /1_55_0/libs/ptr_container/doc/ptr_list.html)? –

    답변

    3

    m_childsshared_ptr, m_parentweak_ptr을 사용할 수 있습니다.

    그러나 원시 포인터를 부모 Node에 유지하고 weak 포인터를 전혀 사용하지 않는 것이 좋습니다. 이것의 뒤에있는 보호 메커니즘은 널이 아닌 부모가 항상 존재한다는 불변입니다.

    또 다른 옵션은 ChainDescriptor에만 shared_ptr을 사용하고 모든 원시 포인터는 Node에 유지합니다. 이 접근법은 약한 포인터를 피하고 깨끗한 소유권 정책을 갖습니다 (부모 노드는 자식을 소유합니다).

    약한 포인터는 자동으로 메모리를 관리하는 데 도움이되지만이 뒷면은 퍼지 소유권 논리 및 성능 저하입니다.

    +2

    +1 원시 포인터의 옵션을 언급합니다. 나는 그들이 m_parent의 소유권 상황을 표현하는 올바른 도구라고 생각한다. – risingDarkness

    +0

    @ Sergey, 동의합니다. 나는이 방향으로 원형을 쓰기 시작했다. ChainDescriptor에 shared_ptr 목록이 포함됩니다. Node 클래스의 경우 weak_ptr을 작성하기 시작했는데 weak_ptr에 no == 연산자가 정의되어 있으므로 목록에서 노드를 검색하는 것이 약간 까다 롭습니다. – Zyend

    +0

    ''weak_ptr''를 통해 parent에 거의 접근하지 않습니까? 그렇다면''weak_ptr''은 가장 친한 친구입니다. 내부 루프의 어딘가에 항상 부모에게 액세스해야한다면 원시 포인터에 대해 생각해보십시오. 그들은 나쁘지 않습니다. –

    2

    shared_ptr은 스마트 포인터를 소유하고 weak_ptr은 스마트 포인터를 참조합니다.

    그래서 상황에서 나는 ChainDescriptorshared_ptr를 사용한다 (이 노드를 소유)와 Node는 (그들 소유) m_childs에 대한 m_parent에 대한 weak_ptr (그것은 단지 그것을 참조)하고 shared_ptr를 사용한다고 생각합니다.

    +0

    'weak_ptr'의 이점을 부모에 대한 원시 포인터보다 설명 할 수 있겠습니까? –

    +1

    'weak_ptr'의 사용은 안전합니다. 원시 포인터의 유효성은 불변량에 의해 보장되어야합니다 (Sergey의 대답에서 설명). 'weak_ptr'의 주된 문제점은 성능에 영향을 미칩니다. 속도가 필요한 경우 원시 포인터와 경쟁 할 수있는 것은 없습니다. – Johny

    +0

    'weak_ptr'의 사용법은 복잡하기 때문에 여기에 실제로 어떤 안전성이 추가되는지는 알지 못합니다. 속도 문제뿐 아니라 사용의 용이성에 대한 질문이기도합니다. 'weak_ptr'가 프로그래머가 실제로 만들지도 모르는 어떤 종류의 오류로부터 보호된다면 그것은 한 가지 일 것입니다. 그러나이 경우 코드는 단일 부모 만 지원하므로 부모를 삭제하면 자식이 삭제되므로 부모를 삭제 한 후에 부모에 대한 포인터에 액세스 할 수있는 경우는 없습니다. –

    1

    통상적 인 구현은 각 노드가 그 자식에 대한 강한 참조를 가지므로 (즉, 그것들을 계속 유지하는) 각 자식은 부모에 대한 약한 참조를 갖는 것이다.

    순환 참조를 피하기위한 것입니다. 강한 참조 만 사용 되었다면 부모 참조 수가 0으로 떨어지지 않는 상황 (자식에게 참조가 있기 때문에)과 자식 참조가 0으로 떨어지지 않는 상황이 있습니다 (부모가 참조를 가지고 있기 때문에).

    여러분의 ChainDescriptor 클래스는 여기에 강력한 참조를 사용하는 것이 좋다고 생각합니다.

    +0

    일반적인 구현에서는 부모에 대한 역 포인터에 원시 포인터를 사용합니다. 소유권 의미론은 자식이 부모보다 오래 살 수 없다는 것을 보장하기 때문에 약한 포인터는 필요 없습니다. 그리고 구조에 따라 : DAG의 경우 자식 포인터를 공유해야하지만 그렇지 않은 경우 'unique_ptr'은 훨씬 적은 복잡성과 오버 헤드로 작업을 수행합니다. –

    +0

    사실 ... 제 생각에 어떤 시점에'Node :: getParent()'함수가 필요하다면, 전체'shared_ptr'을 반환하기를 원할 것입니다. 'enable_shared_from_this'를 사용하는 것보다 "weak_ptr"을 "업그레이드"합니다. –

    +0

    @Tristan, 물론 동의합니다. ChainDescriptor는 전체 노드 목록을 포함합니다. 그러나 ChainDescriptor가 노드 (즉, chainDesc.removeNode ("blabla"))를 제거하면 기본 목록뿐만 아니라 "blabla"를 자식으로 갖는 다른 모든 노드에서도 제거됩니다. – Zyend

    1

    생크 포인터를 똑같은 종류의 스마트 포인터로 대체하려고 시도하면 포인터는 일반적으로 이 작동하지 않습니다. 스마트 포인터는 약한 포인터와 다른 의미로 을 가지며, 일반적으로이 특수 의미를 더 높은 수준에서 고려해야합니다. 여기서 "가장 깨끗한"솔루션은 복사 에 대한 지원을 ChainDescriptor에 추가하여 전체 복사본을 구현하는 것입니다. (여기서 을 복제 할 수 있으며 Node을 복제 할 수 있으며 모든 Node은 항상 ChainDescriptor에 의해 소유 된 입니다.) 또한 실행 취소를 위해 에 어쨌든 깊은 복사본이 필요할 수 있습니다. 실행 취소를 위해 저장된 데이터를 수정하려면 활성 인스턴스의 수정을 원하지 않습니다.

    당신의 노드는 나무를 형성하는 데 사용되는 것 같습니다. 이 경우, std::shared_ptr는 1만큼, 작동) 모든 Node 항상 의해 "소유"하는 중 하나 ChainDescriptor 또는 부모 Node, 2) 구조가 정말 숲, 또는 적어도 DAG의 모음 (물론 저장된 모든 인스턴스에서 을 변경하지 않는 경우). 구조가 주기가 발생할 수있는 경우 수준에서 shared_ptr을 사용할 수 없습니다. 노드 목록과 트리를 별도의 구현 클래스로 추상화하고 ChainDescriptorshared_ptr으로 유지할 수 있습니다.

    (FWIW가 :. 나는 참조 의 노드에 대한 구문 분석 내가 몇 년 전에 쓴 나무, 다른 인스턴스 하위 트리를 공유 할 수를 포인터를 계산 사용하지만 사용하는 처음부터 그것을 설계 참조 포인터를 계산 그리고 트리가 어떻게 구성 되었는가에 따라 사이클이 없음을 보장 받았다.)

    +0

    예, 노드는 항상 체인 또는 상위 노드에 연결됩니다. 그러나 노드가 체인에서 제거되면 (즉, chain.removeNode ("blabla")), 메커니즘은 노드를 소유 한 노드의 하위 목록에서도 제거해야합니다. – Zyend

    +0

    @Zyend 어떤 스마트 포인터도 모르겠습니다. 부모에게로 이동해야하고 그 노드도 제거해야한다는 뜻입니까? –

    +0

    @Zyend 또한 실제로 실행 취소 목록에 넣는 것은 무엇이며 어떻게 할 것입니까? 귀하의 설명에서 깊은 사본이 필요하다는 매우 강한 인상을 받게됩니다 (아마도 개체는 현재 복사 할 수 없습니다). –