Endlich habe ich sehr seltsamen Bug, der durch doppelte Aufruf Destruktor verursacht wird, aufgespürt. Hier ist der minimale Code, der den Fehler reproduziert:Seltsame doppelte Destruktor Aufruf bei der Verwendung von 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);
}
Die Ausgabe lautet:
constructor: 0x7fffffffe67f
destructor: 0x7fffffffe2df
destructor: 0x7fffffffe67f
Wie Sie sehen, der erste destructor ist unerwünscht und wird auf verschiedenen Objekt aufgerufen, die bei nicht gebaut wurde alle (die Adresse ist anders), aber in meinem echten Code ist die Adresse nah genug und es verdirbt die Container, die ich im Event-System habe.
Debugging zeigt, dass make_shared den Destruktoraufruf verursacht.
Was verursacht diesen unerwünschten Destruktoranruf und wie kann ich ihn loswerden? Ich benutze g ++ 4.7 mit C++ 11 Flagge.
Das Problem ist, dass die unerwünschte Destruktoraufrufs in der Regel (90% der Zeit) verdirbt mein Ereignissystem Container in meinem realen Code, die segfaults verursacht, aber es funktioniert nicht selten verderben und alles funktioniert.
Mann, du bist ein Held, es funktioniert jetzt in meinem echten Code! Aber warum temporäre Kopie ist so unsicher, dass es meine Daten im Speicher beschädigen kann? Ich gehe fast immer durch Verweis (das war ein Fehler), aber es scheint mir zumindest seltsam. – user1873947
@ user1873947, da der Kopierkonstruktor Compiler generiert wird, ist es wahrscheinlich die falsche Sache. Wenn es zum Beispiel eine Kopie eines Zeigers erstellt und der Destruktor es dann löscht, verbleibt ein ungeordneter Zeiger im ursprünglichen Objekt. –
@Mark Ransom, das ist es. In meinem echten Code habe ich eine Reihe von Zeigern. – user1873947