'enable_shared_from_this'의 유용성은 무엇입니까?
-
23-08-2019 - |
문제
나는 가로 질러 달렸다 enable_shared_from_this
boost.asio 예제를 읽고 문서를 읽은 후에는 이것이 어떻게 올바르게 사용되어야하는지에 대해서도 여전히 길을 잃었습니다. 누군가 나 에게이 수업을 사용하는 시점에 대한 예제 및/또는 설명이 의미가 있습니다.
해결책
유효한 것을 얻을 수 있습니다 shared_ptr
인스턴스 this
, 당신이 가진 모든 것일 때 this
. 그것 없이는 당신은 shared_ptr
에게 this
, 당신이 이미 회원을 가지고 있지 않는 한. 이 예제에서 ENABLE_SHARED_FROM_THIS에 대한 문서를 부스트하십시오:
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 ()가 유효한 것을 반환합니다 shared_ptr
, 멤버 인스턴스가 없더라도. 단순히 이것을 할 수는 없습니다.
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
이것이 반환 된 공유 포인터는 "적절한"과 다른 참조 수를 가질 것이며, 그 중 하나는 객체를 삭제할 때 매달려있는 참조를 잃고 유지하게됩니다.
enable_shared_from_this
C ++ 11 표준의 일부가되었습니다. 부스트뿐만 아니라 거기에서도 얻을 수 있습니다.
다른 팁
약한 포인터에 관한 Dobbs Dr Article 에서이 예는 이해하기 쉽다고 생각합니다 (출처 : http://drdobbs.com/cpp/184402026):
... 이와 같은 코드는 올바르게 작동하지 않습니다.
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
둘 중 어느 것도 shared_ptr
물체는 다른 것에 대해 알고 있으므로 둘 다 파괴 될 때 자원을 해제하려고합니다. 일반적으로 문제가 발생합니다.
마찬가지로, 멤버 함수에 필요한 경우 a 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_pt
R 객체 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
.
따라서 처음 a를 만들 때 shared_ptr<T>
새로운 t*에, 그 t*의 내부 약점 _ptr은 1의 refcount로 초기화됩니다. shared_ptr
기본적으로 이것으로 돌아갑니다 weak_ptr
.
T
그러면 그 방법으로 전화 할 수 있습니다 shared_from_this
인스턴스를 얻으려면 shared_ptr<T>
저것 내부적으로 저장된 참조 수로 뒤로. 이런 식으로, 당신은 항상 하나의 장소가 있습니다 T*
의 ref-count는 여러 가지가 아닌 저장됩니다 shared_ptr
서로에 대해 모르는 인스턴스와 각각은 shared_ptr
그것은 Ref-Counting을 담당합니다 T
그들의 ref-count가 0에 도달하면 그것을 삭제합니다.
Boost :: Instrusive_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;
};
또 다른 방법은 a를 추가하는 것입니다 weak_ptr<Y> m_stub
회원 class Y
. 그런 다음 작성하십시오.
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
당신이 얻는 수업을 변경할 수 없을 때 유용합니다 (예 : 다른 사람들의 도서관 확장). 회원을 초기화하는 것을 잊지 마십시오. m_stub = shared_ptr<Y>(this)
, 생성자 중에도 유효합니다.
상속 계층 구조 에이 스터브가 더 많으면 대상의 파괴를 방해하지 않습니다.
편집하다: 사용자 NoBar가 올바르게 지적했듯이, 할당이 완료되고 임시 변수가 파괴 될 때 코드는 y 객체를 파괴합니다. 따라서 내 대답이 잘못되었습니다.