2013-02-06 9 views
5

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.

Antwort

11

Der CTileBrowser-Konstruktor nimmt sein Argument nach Wert. Sie sehen wahrscheinlich die Zerstörung einer temporären Kopie, die für diesen Konstruktor erstellt wurde. Ändern Sie es zu einem Referenzparameter und ich wette, das Problem wird verschwinden.

+0

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

+2

@ 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. –

+0

@Mark Ransom, das ist es. In meinem echten Code habe ich eine Reihe von Zeigern. – user1873947

Verwandte Themen