2013-02-06 2 views
5

마지막으로 소멸자를 두 번 호출하여 발생하는 매우 이상한 버그를 추적했습니다.shared_ptr을 사용할 때 이상한 이중 소멸자 호출

#include <iostream> 
#include <memory> 
#include <set> 

class cEventSystem { 
    public: 
     cEventSystem() { 
      std::cout << "constructor: " << this << std::endl; 
     } 
     ~cEventSystem() { 
      std::cout << "destructor: " << this << std::endl; 
     } 
}; 

class cSubscriber { 
    public: 
     cSubscriber(cEventSystem& eventSystem) : eventSystem(eventSystem) {} 
     virtual ~cSubscriber() {} 
     virtual void onEvent() = 0; 
    protected: 
     cEventSystem& eventSystem; 
}; 

class cTileBrowser: public cSubscriber { 
    public: 
     cTileBrowser(cEventSystem eventSystem) : cSubscriber(eventSystem) {} 
     void onEvent() {} 
}; 

class cGui: public cSubscriber { 
    public: 
     cGui(cEventSystem& eventSystem) : cSubscriber(eventSystem) { 
      tileBrowser = std::make_shared<cTileBrowser>(eventSystem); 
     } 
     void onEvent() {} 
     std::shared_ptr<cTileBrowser> tileBrowser; 
}; 

int main() { 
    cEventSystem eventSystem; 
    cGui gui(eventSystem); 
} 

출력은 다음과 같습니다 : 여기에 버그를 재현 최소한의 코드는

constructor: 0x7fffffffe67f 
destructor: 0x7fffffffe2df 
destructor: 0x7fffffffe67f 

첫 번째 소멸자가 원하지 않는과가에 건설되지 않은 다른 객체라고 볼 수 있듯이 모두 (주소는 다르다). 그러나 내 실제 코드에서 주소는 충분히 가깝고 이벤트 시스템에있는 컨테이너를 손상시킨다.

디버깅은 해당 소멸자 호출을 발생시키는 make_shared임을 보여줍니다.

불필요한 소멸자 전화가 발생하는 이유는 무엇이며 어떻게 제거합니까? g ++ 4.7을 C++ 11 플래그와 함께 사용합니다.

문제는 원치 않는 소멸자 호출 일반적으로 (시간의 90 %) 세그먼테이션 폴트 (segfault)를 발생 내 실제 코드 내 이벤트 시스템 컨테이너를 손상한다는 것입니다,하지만 거의 그것을 손상하지하지 않고, 모든 것이 작동합니다.

답변

11

CTileBrowser 생성자가 값으로 인수를 사용 중입니다. 생성자에 대해 생성 된 임시 복사본이 삭제 된 것을 볼 수 있습니다. 참조 매개 변수로 변경하면 문제가 사라질 것입니다.

+0

남자, 당신은 영웅입니다, 제 실제 코드에서 작동합니다! 하지만 왜 임시 복사본이 너무 안전하지 않아서 메모리에있는 내 데이터가 손상 될 수 있습니까? 나는 거의 항상 참고 문헌 (이 것은 실수였다.)을 통과하지만 적어도 나에게는 이상하게 보인다. – user1873947

+2

@ user1873947, 복사 생성자가 컴파일러로 생성되었으므로 아마 잘못된 것입니다. 예를 들어 포인터의 복사본을 만들고 소멸자가 삭제하면 원본 개체에 매달린 포인터가 남아 있습니다. –

+0

@Mark Ransom 그게 전부입니다. 내 실제 코드에는 포인터 집합이 있습니다. – user1873947

관련 문제