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.
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
@ 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. –
@ Mark Ransom to wszystko. W moim prawdziwym kodzie mam zestaw wskaźników. – user1873947