2015-06-04 6 views
5

Der folgende CodeBoost-shared pointer Konstruktor destructor

struct Base 
{ 
    public: 
    Base() 
    { 
    std::cout<<"Base Ctr"; 
    } 

    ~Base() 
    { 
    std::cout<<"Base Dtr"; 
    } 
}; 

struct Derived : Base 
{ 
    Derived() 
    { 
    std::cout<<"Derived Ctr"; 
    } 

    ~Base() 
    { 
    std::cout<<"Derived Dtr"; 
    } 
}; 

int main() 
{ 
    Base* b = new Derived; 
    delete b; 
} 

gibt mir die folgende Ausgabe:

Base Ctr 
Derived Ctr 
Base Dtr 

Die Lösung hierfür ist die Basis Destruktor virtuell zu machen.

Allerdings, wenn ich Boost Smart Pointer ohne virtuelle Basis Destruktor verwenden. Ich bekomme eine andere Ausgabe.

int main() 
{ 
    boost::shared_ptr<Base> b = boost::make_shared<Derived>(); 
} 

Der Ausgang ist

Base Ctr 
Derived Ctr 
Derived Dtr 
Base Dtr 

Wie Shared_ptr der Lage ist, zu steigern, dies zu erreichen, ohne zu beeinflussen (Ich gehe davon aus), um die Basis und Abgeleitete Klassen.
Wie skaliert es für mehrstufige Vererbung, d. H. Basispunkte zu Dervderv wo Dervderv von Derv geerbt wird.

EDIT:

meisten Antworten mir sagen, dass die "Magie" geschieht in make_shared. Ich bekomme jedoch das gleiche Verhalten für den folgenden Code

boost::shared_ptr<Base> ptr(new Derived); 
+0

Ihre Klassen haben keine virtuellen Destruktoren. ist das nicht undefiniertes Verhalten? –

+0

Genau der Punkt, den ich versuche herauszufinden. Dieses Verhalten ist nicht im Fall von Boost-Smartpointern definiert –

+1

@PranavKapoor Sie können mein Beispiel ändern, indem Sie der Pointer-Klasse einen weiteren Konstruktor hinzufügen: \t Vorlage expliziter Zeiger (Y * p): p_ (p), Deleter_ (Std :: bind (& default_deleter , p)) {} – nikitoz

Antwort

3

Eine Lösung implementiert normale Funktion Zeiger verwendet werden:

#include <iostream> 
#include <boost/shared_ptr.hpp> 
#include <boost/make_shared.hpp> 
#include <typeinfo> 

using namespace std; 

struct Base{ 

    Base(){ 
    cout<<"Base ctor."<<endl; 
    } 

    ~Base(){ 
    cout<<"Base dtor."<<endl; 
    } 

}; 

struct Derv: Base{ 

    Derv():Base(){ 
    cout<<"Derv ctor."<<endl; 
    } 

    ~Derv(){ 
    cout<<"Derv dtor."<<endl; 
    } 

}; 

typedef void (*DEL)(void*); 

template<typename U> 
void deleter(void* ptr) 
{ 
    delete static_cast<U*>(ptr); 
} 

template<typename T> 
struct SmartPtr{ 

    T* memPtr; 

    DEL func; 

    template<typename U> 
    SmartPtr(U* p): 
     memPtr(p) 
    { 
    func = deleter<U>; 
    } 

    ~SmartPtr(){ 

     func(memPtr); 
    } 
}; 
int main() 
{ 
    //case 1 
    SmartPtr<Base> ptrSmart1(new Derv()); 

    //case 2 
    SmartPtr<Base> ptrSmart2(new Base()); 

    //case 3 
    SmartPtr<Derv> ptrSmart3(new Derv()); 

    return 0; 
} 
2

Boost erreicht, dass mit einem Deleter Funktor. Die Standardimplementierung ruft delete für den Typ auf, der mit make_shared erstellt wurde.

+0

ein guter Artikel zum Thema: https://marcoarena.wordpress.com/2014/04/12/ponder-the-use-of-unique_ptr-to-enforce-the-rule-of-zero/ –

3

Kurz gesagt, enthält boost::smart_ptr Zeiger auf ein Objekt, Referenzzähler und eine deleter-Funktion, die in Destruktor aufgerufen wird, wenn Sie boost::make_shared<Derived>() nennen, wird es standardmäßig konstruierten Objekt der Klasse Derived und deleter zu einem Destruktor wird darauf erstellen Derived. Dies sollte für eine beliebig lange Vererbungskette funktionieren, aber ein virtueller Destruktor in der Basisklasse ist zwingend erforderlich.

Betrachten wir nun naiv Beispiel Implementierung solcher Technik vereinfacht:

#include <iostream> 
#include <functional> 
using namespace std; 

struct Base 
{ 
    public: 
    Base() 
    { 
    std::cout<<"Base Ctr "; 
    } 

    ~Base() 
    { 
    std::cout<<"Base Dtr "; 
    } 
}; 

struct Derived : Base 
{ 
    Derived() 
    { 
    std::cout<<"Derived Ctr "; 
    } 

    Derived(const Derived& d) 
    { 
    std::cout<<"Derived copy Ctr "; 
    } 

    ~Derived() 
    { 
    std::cout<<"Derived Dtr "; 
    } 
}; 

template <typename T> 
void default_deleter(T* p) { 
    delete p; 
} 

template <typename T> 
struct pointer { 
    pointer(T* p, std::function<void()> d) : p_(p), deleter_(d) {} 
    template <typename U> 
    pointer(pointer<U> other) : p_(new U(*other.p_)), 
    deleter_(std::bind(&default_deleter<U>, (U*)p_)) {} 
    template <typename Y> 
    explicit pointer(Y* p) : p_(p), deleter_(std::bind(&default_deleter<Y>, p)) {} 
    ~pointer() { 
     deleter_(); 
    } 
    T* p_; 
    std::function<void()> deleter_; 
}; 


template <typename T> 
pointer<T> make_pointer() { 
    T* p = new T; 
    return pointer<T>(p, std::bind(&default_deleter<T>, p)); 
} 

int main() { 
    pointer<Base> b = make_pointer<Derived>(); 
    pointer<Base> b2(new Derived); 
    return 0; 
} 

Die Ausgabe lautet:

Base Ctr Derived Ctr Base Ctr Derived copy Ctr Derived Dtr Base Dtr Derived Dtr Base Dtr 

Destruktoren der abgeleiteten Klasse werden zweimal genannt, weil es zwei Instanzen Zeiger sind: eine, die war erstellt in make_pointer Funktion und der zweite ist in der Hauptfunktion.

+0

nach Ihrem Ausgabe Dtrs sind zweimal aufgerufen, können Sie erklären? – Nik

+0

@Nik Ja, die Antwort wurde aktualisiert. – nikitoz