2015-02-05 1 views
7
#include <memory> 
#include <iostream> 

struct A : public std::enable_shared_from_this<A> 
{ 
    ~A() 
    { 
     auto this_ptr = shared_from_this(); // std::bad_weak_ptr exception here. 
     std::cout << "this: " << this_ptr; 
    } 
}; 

int main() 
{ 
    auto a = std::make_shared<A>(); 
    a.reset(); 
    return 0; 
} 

shared_from_this()을 호출 할 때 std::bad_weak_ptr 예외가 발생합니다. 그것은 설계에 의한 것인가? 그렇습니다. 소멸자가 반환 된 후에는이 포인터를 사용할 수 없으므로 위험 할 수 있습니다. 그러나 공유 포인터 객체가 아직 존재하고 있기 때문에 포인터를 가져 오는 것이 기술적으로 불가능한 이유는 없습니다. 익숙한. 내 자신의 enable_shared_from_this 아날로그 (필자는 그렇게하지 않을 것)를 작성하는 것만 큼이 방법을 사용할 수 있습니까?std :: enable_shared_from_this : 소멸자에서 shared_from_this()를 호출 할 수 있습니까?

+0

http://stackoverflow.com/q/8501503/1147772 – Drax

+0

@Drax : 그 질문을 보았습니다. 'std'가 아니라 'boost'에 관한 것이고, 그 대답은'shared_from_this()'가용성에 대한 주요 제한 사항이 아니라 문제의 코드의 특정 디자인에 관한 것입니다. –

+0

@VioletGiraffe 질문은 '부스트'도 아니고 '표준'도 아니며, 약한 레퍼런스의 개념에 불과합니다. – curiousguy

답변

6

여기에 포인터를 가져 오는 것이 기술적으로 불가능한 이유는 알지 못합니다. 공유 포인터 개체가 분명히 존재하며 사용할 수 있기 때문입니다.

매우 기술적 인 이유가 있습니다.

shared_ptr이있을 수 있지만 A 개체의 참조 횟수가 0에 도달 했으므로 소멸자가 실행되고있는 것입니다. 참조 횟수가 0이되면 다시 증가시킬 수 없습니다 (그렇지 않으면 소멸자가 실행 중이거나 이미 삭제 된 객체를 나타내는 shared_ptr을 얻을 수 있습니다).

shared_from_this()을 호출하면 참조 횟수가 증가하고 현재 소유자와 소유권을 공유하는 shared_ptr가 반환되지만 카운터를 0에서 1로 늘릴 수 없으므로 실패합니다. 에서

(객체의 소멸자 내부)이 매우 특별한 경우 당신이 개체가 아직 완전히 파괴되지 않은 알고 있지만, shared_from_this() 함수를 호출 누가 알 수있는 방법을 enable_shared_from_this<A>이 없다, 그래서인지 알 수 없다 이 매우 구체적인 경우 또는 오브젝트의 소멸자 외부의 일부 코드 (예 : 소멸자를 계속 진행할 다른 스레드)에서 발생합니다.

어떻게 든이 특수한 경우에 사용할 수 있고 현재 파괴 된 개체를 참조한 shared_ptr<A>이있는 경우 나중에 사용하기 위해 저장 한 소멸자 외부에 shared_ptr을 부여 할 수 있습니다. 이렇게하면 객체가 파괴 된 후 다른 코드가 매달려 shared_ptr에 액세스 할 수 있습니다. 이는 shared_ptrweak_ptr 유형 시스템에서 큰 구멍이됩니다.

+0

위대한 설명, 고마워요. –

0

shared_ptr::reset의 구현은 종종 shared_ptr().swap(*this)입니다.

즉, 복사하려는 shared_ptr은 이미 소멸자를 호출하기 전에 공유 횟수를 감소시키는 소멸자 상태입니다. 당신이 enable_shared_from_this를 호출 할 때 카운트가 0

그래서 귀하의 질문에 대답하는 경우는 예외가 발생하는 weak_ptr에서 shared_ptr를 구성하여 weak_ptr가 저장된 촉진하기 위해 노력할 것입니다,하고의 표준 방법이 없다 무엇 표준 라이브러리 구현이 권한을 부여하는 방식으로 작동하지 않는 경우 (표준에 의해 위임되었는지 여부는 알 수 없음) 원하는 경우입니다.

이제

, 여기에 내 컴퓨터 (연타/libc의 ++)에서 작동하는 해킹 :

#include <memory> 
#include <iostream> 

class hack_tag 
{ 
}; 

namespace std 
{ 

    template<> 
    class shared_ptr<hack_tag> 
    { 
    public: 
    template<typename T> 
    weak_ptr<T>  extract_weak(const enable_shared_from_this<T>& shared) 
    { 
     return shared.__weak_this_; 
    } 
    }; 

}; 

using weak_ptr_extractor = std::shared_ptr<hack_tag>; 

class test : public std::enable_shared_from_this<test> 
{ 
public: 
    test() 
    { 
    std::cout << "ctor" << std::endl; 
    } 

    ~test() 
    { 
    std::cout << "dtor" << std::endl; 
    weak_ptr_extractor hacker; 
    auto weak = hacker.extract_weak(*this); 
    std::cout << weak.use_count() << std::endl; 
    auto shared = weak.lock(); 
    } 
}; 


int  main(void) 
{ 
    std::shared_ptr<test> ptr = std::make_shared<test>(); 

    ptr.reset(); 
} 

하지만 난 당신이 당신이 복사 shared_ptr을 소유하기 때문에 그와 유용한 아무것도 할 수 있는지에 관한 것입니다 아니에요 그 사본은 reset 전화가 걸린 후에 새로운 깨끗한 것 shared_ptr과 공유하지 않습니다.

9

[util.smartptr.enab]/7 shared_from_this위한 전제 조건을 설명

이 필요합니다enable_shared_from_this<T>T의 액세스 기본 클래스이어야한다. *thistT 인 객체의 하위 객체 여야합니다. &t을 소유 한 shared_ptr 인스턴스 p이 하나 이상 있어야합니다. [emph. 추가]

개체가 파괴 되었기 때문에 소유하고있는 개체가 shared_ptr이 아니어야합니다. 따라서 해당 요구 사항을 위반하지 않고 shared_from_this으로 전화를 걸면 정의되지 않은 동작이 발생할 수 있습니다.

+1

"당신의 객체가 파괴되기 때문에 소유하고있는'shared_ptr'이 없다는 것이어야합니다."누군가가 명시적인 소멸자 호출을하기에 충분히 미치지 않는다면) –

+1

@ T.C. 그렇다면 "누군가"는 분명히 그 객체를 가리키는'shared_ptr'이 아니라 * owner가 실제로 소유자라고 생각합니다. 내 생각은 의미한다;) – Casey

+0

내 생각에'shared_ptr'은 객체를 파괴하는 책임이 있기 때문에, 보유한 객체가 삭제 될 때까지 포인터는 파괴되지 않는다. 하지만 분명히 참조 카운터 객체는 적어도 MS 구현에서 삭제되기 전에 이미 소멸자에서 사용할 수 없습니다. –

관련 문제