2013-02-06 14 views
5

W końcu znalazłem bardzo dziwny błąd, który jest spowodowany przez podwójne wywołanie destruktora. Oto kod, który odtwarza minimalny błąd:Dziwne podwójne wywołanie destruktora przy użyciu 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); 
} 

wyjście jest:

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

Jak widać pierwsze destructor jest niepotrzebny i to się nazywa na inny obiekt, który nie został zbudowany na wszystko (adres jest inny), ale w moim prawdziwym kodzie adres jest wystarczająco blisko i psuje kontenery, które mam w systemie zdarzeń.

Debugowanie pokazuje, że jest to make_shared, co powoduje wywołanie destruktora.

Co powoduje wywołanie niechcianego destruktora i jak mogę się go pozbyć? Używam g ++ 4.7 z flagą C++ 11.

Problem polega na tym, że niechciany wywołanie destruktora zwykle (90% razy) psuje moje kontenery systemu zdarzeń w moim prawdziwym kodzie, co powoduje uszkodzenia, ale rzadko nie powoduje ich uszkodzenia i wszystko działa.

Odpowiedz

11

Konstruktor CTileBrowser przyjmuje argument po wartości. Prawdopodobnie widzisz zniszczenie tymczasowej kopii utworzonej dla tego konstruktora. Zmień go na parametr referencyjny i założę się, że problem zniknie.

+0

Człowieku, jesteś bohaterem, działa teraz w moim prawdziwym kodzie! Ale dlaczego tymczasowa kopia jest tak niebezpieczna, że ​​może uszkodzić moje dane w pamięci? Prawie zawsze przechodzę przez referencję (ta była błędem), ale wydaje mi się to co najmniej dziwne. – user1873947

+2

@ user1873947, ponieważ konstruktor kopiowania jest generowany przez kompilator, prawdopodobnie robi źle. Jeśli na przykład tworzy kopię wskaźnika, a destruktor usuwa go, w oryginalnym obiekcie pozostanie zwisający wskaźnik. –

+0

@ Mark Ransom to wszystko. W moim prawdziwym kodzie mam zestaw wskaźników. – user1873947