2017-03-28 4 views
0

Ich hatte ein Szenario in C++, das den Destruktor des Kindes in einem Fall aufruft, in dem ich es nicht erwartet hatte. Eine minimale Repro ist unten:Elternklasse mit Standardkonstruktor; Der Destruktor der Kindklasse wird unerwartet aufgerufen

#include <cstdio> 
#include <memory> 

using namespace std; 

class Parent { 
public: 
}; 

class Child : public Parent { 
    public: 
    ~Child() { 
     printf("Got here\n"); 
    } 
}; 

int 
main() 
{ 
    shared_ptr<Parent> x(new Child); 
} 

Normalerweise ist so etwas wie ein Fehler. Der Entwickler beabsichtigt, dass der Child-Destruktor aufgerufen wird und die korrekte Aktion darin besteht, einen leeren virtuellen Destruktor in den Parent einzufügen. Zu meinem Schrecken, sowohl G ++ 4.4.7 (ja, ich weiß, dass es alt ist) und clang 3.4.2 kompilieren Sie das so, dass der Kinddestruktor genannt wird.

Entspricht dies dem Standard?

+0

@chris ja Ich habe überprüft, es tut das Ding, nicht sicher, ob es überhaupt nützlich ist. –

+0

@ n.m., Ich kann nicht sagen, dass ich persönlich einen Bedarf dafür hatte, aber es ist eine gute Sache, wenn du es tust. – chris

+0

@ n.m. Es ist nützlich, weil Sie 'shared_ptr ' verwenden können, um abgeleitete Objekte zu verwalten, ohne den Aufwand eines virtuellen Destruktors –

Antwort

5

Nun, auch wenn shared_ptr keine spezielle Magie, delete ing durch Elternzeiger mit nicht-virtuellen Destruktor ist nur undefiniert Verhalten, so dass die Ergebnisse (des Aufrufs des Kindes Destruktor) würde definitiv entsprechen.

Aber in diesem Fall "merkt" shared_ptr den Typ des ursprünglichen Objekts, das Sie in es übergeben und zerstört es durch untergeordneten Zeiger (durch seine gespeicherte Deleter).

+0

Ähm, verwendet der gespeicherte Deleter nicht den Schablonentyp und nicht den Typ, mit dem er erstellt wurde? – NathanOliver

+0

Nein, ungefähr speichert es eine 'std :: function' (oder äquivalent) zu' deleter ', wobei' U' der Parametertyp des Konstruktors ist, nicht der 'shared_ptr'-Schablonentyp. Es funktioniert sogar auf 'shared_ptr ' usw. –

0

Ein einzigartiges PTR würde hier vermasseln. Aber Shared Ptr macht hier "Magie".

shared_ptr<T> has two things they manage; the T * `und der Referenzzählerblock.

Der Referenzzählblock enthält zwei atomic<std::size_t> s, einen für starke Referenzen und einen für schwache Referenzen und einen Deleeter vom Typ gelöscht. Diese std::function-ähnliche Art gelöscht Delet merkt sich, wie Sie die Sache, die Sie besitzen löschen.

Wenn Sie einen shared_ptr mit einem U*u konstruieren, speichert er standardmäßig [u]{std::default_delete<U>{}(u);} in diesem Deleter.

In der Tat erinnert es, wie das Objekt löschen basierend auf dem Typ übergeben.

shared_ptr äußerst flexibel ist.

Sie können in einem benutzerdefinierten deleter passieren den Standard zu ersetzen, können Sie den Alias-Konstruktor verwenden, um die Referenzzählung Block aus den gespeicherten T* verwendet aufzuzuspalten, Sie make_shared verwenden können die Referenzzählung Block und ein T in der zuzuteilen gleiche Speicherzuweisung.

Der Overhead des Referenzzählblocks ist der Grund, warum der Deleter gespeichert wird; Es wurde nicht als zu teuer angesehen, da wir den Block sowieso brauchten. Im Vergleich dazu unique_ptr standardmäßig nicht so etwas; Sie müssen explizit einen Deleter hinzufügen, und Sie müssen alle phantastischen Tricks verwalten, die shared_ptr für Sie standardmäßig ausführt, wenn Sie sie haben wollten. unique_ptr hat im Grunde Null Overhead über eine Roh-Besitzer-Zeiger; shared_ptr hat einen bemerkenswerten Overhead, aber im Vergleich zum Overhead der Speicherzuweisung normalerweise klein.

Verwandte Themen