`enable_shared_from_this`의 유용성은 무엇입니까? 대한 분실하고있는 문서를 읽고. 누군가이 수업을 사용할

나는 가로 질러 enable_shared_from_thisBoost.Asio 예제를 읽는 동안 나는 여전히 올바르게 사용하는 방법에 대한 분실하고있는 문서를 읽고. 누군가이 수업을 사용할 때의 설명과 설명을 해 줄 수 있습니까?



답변

당신이 가진 모든 경우에 유효한 shared_ptr인스턴스 를 얻을 수 있습니다 . 그게 없으면, 당신은을 얻는 방법이없는 것 에 이미 구성원으로 일했다하지 않는 한을. enable_shared_from_this에 대한 부스트 문서 의이 예제는 다음과 같습니다.thisthisshared_ptrthis

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

멤버 인스턴스가 f()없더라도이 메소드 는 valid를 리턴 shared_ptr합니다. 간단히 이렇게 할 수는 없습니다.

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

이것에 의해 반환 된 공유 포인터는 “적절한”참조와는 다른 참조 카운트를 가지게되며, 그중 하나는 객체가 삭제 될 때 매달려있는 참조를 잃어 버릴 수 있습니다.

enable_shared_from_thisC ++ 11 표준의 일부가되었습니다. 부스트뿐만 아니라 거기에서도 얻을 수 있습니다.


답변

약한 포인터에 대한 Dr Dobbs 기사 에서이 예제를 이해하기 쉽다고 생각합니다 (출처 : http://drdobbs.com/cpp/184402026 ).

… 이와 같은 코드는 올바르게 작동하지 않습니다.

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

둘 중 어느 것도 shared_ptr 개체 다른 개체에 대해 알지 못하므로 개체가 모두 파괴되면 리소스를 해제하려고합니다. 일반적으로 문제가 발생합니다.

마찬가지로 멤버 함수에 호출되는 shared_ptr객체를 소유 한 객체가 필요한 경우 즉시 객체를 만들 수 없습니다.

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

이 코드는 이전 예제와 같은 문제가 있지만 더 미묘한 형식입니다. 그것이 구축 될 때, shared_ptr 객체 sp1는 새로 할당 된 리소스를 소유합니다. 멤버 함수 내부의 코드 S::dangerous는 해당 shared_ptr객체 에 대해 알지 못 하므로 shared_ptr반환 되는 객체는와 다릅니다 sp1. 새 shared_ptr객체를 복사 sp2해도 도움이되지 않습니다. 때 sp2범위를 벗어나, 그것은 리소스를 분리 한 경우 것이다 sp1범위를 벗어나, 다시 리소스를 해제합니다.

이 문제를 피하는 방법은 클래스 템플릿을 사용하는 것 enable_shared_from_this입니다. 템플리트는 하나의 템플리트 유형 인수를 사용하는데, 이는 관리 자원을 정의하는 클래스의 이름입니다. 해당 클래스는 템플릿에서 공개적으로 파생되어야합니다. 이처럼 :

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

이 작업을 수행 할 때 호출 shared_from_this하는 shared_ptr객체 는 객체 가 소유해야 합니다. 작동하지 않습니다.

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}


답변

여기에 볼트와 너트 관점에서 내 설명이 있습니다 (최고의 답변은 나와 ‘클릭’하지 않았습니다). * 이것은 Visual Studio 2012와 함께 제공되는 shared_ptr 및 enable_shared_from_this의 소스를 조사한 결과입니다. 아마도 다른 컴파일러는 enable_shared_from_this를 다르게 구현합니다 … *

enable_shared_from_this<T>weak_ptr<T>인스턴스에 T대한 ‘ 하나의 참 참조 카운트 ‘를 보유 하는 프라이빗 인스턴스를 추가합니다 T.

따라서 shared_ptr<T>새로운 T *에 처음으로를 생성하면 해당 T *의 내부 weak_ptr이 1의 참조 횟수로 초기화됩니다. 새로운 shared_ptr기본적으로이로 돌아 weak_ptr갑니다.

T그리고, 그 방법으로 호출 할 수 shared_from_this의 인스턴스를 획득하기 위해 shared_ptr<T>같은 내부에 저장된 레퍼런스 카운트에 뒤를 . 이렇게하면 항상 서로에 대해 알지 못하는 T*여러 shared_ptr인스턴스가 아닌 참조 횟수가 저장 되는 한 곳이 있으며 , 각 shared_ptr참조 횟수는 참조 횟수를 계산 T하고 참조 할 때 삭제하는 담당자 라고 생각합니다. -카운트가 0에 도달합니다.


답변

boost :: intrusive_ptr을 사용하면이 문제가 발생하지 않습니다. 이것은 종종이 문제를 해결하는 더 편리한 방법입니다.


답변

c ++ 11 이상에서 정확히 동일 합니다. 원시 포인터를 제공 this하므로 공유 포인터 로 반환 this할 수 있습니다.

다시 말해, 이처럼 코드를 바꿀 수 있습니다

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

이것으로 :

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           


답변

다른 방법은에 weak_ptr<Y> m_stub멤버 를 추가하는 것 class Y입니다. 그런 다음 다음을 작성하십시오.

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

파생하는 수업을 변경할 수없는 경우에 유용합니다 (예 : 다른 사람의 도서관 확장). 멤버를 초기화하는 것을 잊지 마십시오 (예 : by m_stub = shared_ptr<Y>(this))는 생성자 중에도 유효합니다.

상속 계층 구조에 이와 같은 스텁이 더 있으면 오브젝트가 손상되지 않습니다.

편집 : 사용자 nobar가 올바르게 지적한 것처럼 코드는 할당이 완료되고 임시 변수가 파괴되면 Y 객체를 파괴합니다. 따라서 내 대답이 잘못되었습니다.


답변