답변:
당신이 가진 모든 경우에 유효한 shared_ptr
인스턴스 를 얻을 수 있습니다 . 그게 없으면, 당신은을 얻는 방법이없는 것 에 이미 구성원으로 일했다하지 않는 한을. enable_shared_from_this에 대한 부스트 문서 의이 예제는 다음과 같습니다.this
this
shared_ptr
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()
없더라도이 메소드 는 valid를 리턴 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 표준의 일부가되었습니다. 부스트뿐만 아니라 거기에서도 얻을 수 있습니다.
std::shared_ptr
A의 생성자를 원시 포인터 경우 는 상속 std::enable_shared_from_this
. Boost의 의미가이를 지원하도록 업데이트 되었는지 여부는 알 수 없습니다.
std::shared_ptr
다른 사람이 이미 관리하고있는 객체를 구성 std::shared_ptr
하면 내부에 저장된 약한 참조를 참조하지 않으므로 정의되지 않은 동작이 발생합니다." 라고 읽습니다 . ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )
shared_ptr<Y> q = p
?
std::make_shared<T>
합니다.
약한 포인터에 대한 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_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
}
shared_ptr<S> sp1(new S);
사용하는 것이 좋습니다. shared_ptr<S> sp1 = make_shared<S>();
예를 들어 stackoverflow.com/questions/18301511/…
shared_ptr<S> sp2 = p->not_dangerous();
여기의 함정 은 처음 호출하기 전에 일반적인 방법으로 shared_ptr을 만들어야shared_from_this()
한다는 것입니다 ! 이것은 정말로 잘못되기 쉽습니다! C ++ 17 전에이다 UB가 전화 shared_from_this()
: 정확히 하나의 shared_ptr의 정상적인 방법으로 생성 된 이전 auto sptr = std::make_shared<S>();
또는 shared_ptr<S> sptr(new S());
. 고맙게도 C ++ 17부터 그렇게하면 던져 질 것입니다.
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- 이전에 공유 된 객체, 즉 std :: shared_ptr <T>에 의해 관리되는 객체에서만 shared_from_this를 호출 할 수 있습니다. 그렇지 않으면 동작이 정의되지 않습니다 (C ++ 17까지) std :: bad_weak_ptr이 (기본적으로 구성된 weak_this에서 shared_ptr 생성자에 의해) 발생됩니다 (C ++ 17부터). . 따라서 실제로 always_dangerous()
공유되었는지 여부에 대한 지식이 필요하기 때문에 이를 호출 해야합니다.
여기에 볼트와 너트 관점에서 내 설명이 있습니다 (최고의 답변은 나와 '클릭'하지 않았습니다). * 이것은 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에 도달합니다.
So, when you first create...
그것이 요구 사항 이기 때문에 (약점 _ptr은 객체 포인터를 shared_ptr ctor에 전달할 때까지 초기화되지 않습니다!)이 요구 사항은 당신이 잘못되면 상황이 심각하게 잘못 될 수 있기 때문입니다 조심하지 마십시오. 호출하기 전에 shared_ptr을 작성하지 않으면 shared_from_this
UB를 얻습니다. 마찬가지로 하나 이상의 shared_ptr을 작성하면 UB도 얻습니다. 어떻게 든 shared_ptr을 정확히 한 번만 작성해야합니다 .
enable_shared_from_this
점은 얻을 수있을 것입니다 때문에 처음부터 부서지기 shared_ptr<T>
A로부터 T*
하지만, 현실에서 당신은 포인터를 얻을 때 T* t
그것은 이미 공유 여부 것에 대해 무엇을 생각하는 일반적으로 안전하지 않고, 잘못된 추측은 UB입니다.
boost :: intrusive_ptr을 사용하면이 문제가 발생하지 않습니다. 이것은 종종이 문제를 해결하는 더 편리한 방법입니다.
enable_shared_from_this
특별히 허용하는 API로 작업 할 수 있습니다 shared_ptr<>
. 내 의견으로는, 그러한 API는 일반적으로 잘못하고 있습니다 (스택에서 더 높은 것이 메모리를 소유하는 것이 더 낫기 때문에) 그러나 그러한 API로 작업 해야하는 경우 이것은 좋은 옵션입니다.
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;
};
shared_ptr
. 인터페이스가 올바른지 확인하고 싶을 수도 있습니다.
std::shared_ptr<Node> getParent const()
일반적으로 NodePtr getParent const()
대신 노출합니다 . 내부 원시 포인터 (가장 좋은 예 : C 라이브러리 처리)에 액세스 해야하는 std::shared_ptr<T>::get
경우이 원시 포인터 접근자가 잘못된 이유로 너무 여러 번 사용했기 때문에 언급하기가 싫습니다.
다른 방법은에 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 객체를 파괴합니다. 따라서 내 대답이 잘못되었습니다.
shared_ptr<>
그것의 포인트를 삭제하지 않는 것을 생산하는 것이라면, 이것은 과잉입니다. 단항 함수 객체가 return shared_ptr<Y>(this, no_op_deleter);
어디에 있고 아무것도하지 않는 no_op_deleter
것을 간단히 말할 수 있습니다 Y*
.
m_stub = shared_ptr<Y>(this)
이것으로부터 임시 shared_ptr을 구성하고 즉시 파괴합니다. 이 문장이 끝나면 this
삭제되고 모든 후속 참조가 매달려 있습니다.
enable_shared_from_this
유지하고 호출 할 때로 weak_ptr
반환됩니다 . 다시 말해서, 당신은 이미 제공 한 것을 복제하고 있습니다. shared_ptr
shared_from_this
enable_shared_from_this