2015-06-04 21 views
5

Il seguente codice dispinta puntatore condiviso costruttore distruttore

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; 
} 

mi dà il seguente risultato:

Base Ctr 
Derived Ctr 
Base Dtr 

La soluzione a questo è quello di rendere il distruttore base virtuale.

Tuttavia quando uso boost puntatori intelligenti senza distruttore di base virtuale. Ottengo un risultato diverso.

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

L'uscita è

Base Ctr 
Derived Ctr 
Derived Dtr 
Base Dtr 

Come è aumentare shared_ptr in grado di raggiungere questo obiettivo senza influire (sto supponendo) la base e classi derivate.
Come scalarlo per ereditarietà a più livelli, ovvero i punti base su dervderv in cui dervderv viene ereditato da derv.

EDIT:

La maggior parte delle risposte mi dicono che la "magia" avviene in make_shared. Tuttavia ottengo lo stesso comportamento per il seguente codice

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

vostre classi non hanno distruttori virtuali. non è questo comportamento indefinito? –

+0

Esattamente il punto che sto cercando di capire. Questo comportamento non è definito in caso di boost smart pointers –

+1

@PranavKapoor puoi modificare il mio esempio aggiungendo un altro costruttore alla classe pointer: \t template \t pointer esplicito (Y * p): p_ (p), deleter_ (std :: bind (& default_deleter , p)) {} – nikitoz

risposta

3

Una soluzione implementata utilizzando normali puntatori a funzione:

#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 consente di ottenere tale operazione con un deltor functor. L'implementazione predefinita chiama delete per il tipo creato utilizzando make_shared.

+0

un buon articolo sull'argomento: https://marcoarena.wordpress.com/2014/04/12/ponder-the-use-of-unique_ptr-to-enforce-the-rule-of-zero/ –

3

In breve, boost::smart_ptr contiene puntatore ad un oggetto, conteggio di riferimento e una funzione deleter, che si chiama in distruttore, quando si chiama boost::make_shared<Derived>(), si creerà oggetto default-costruito di classe Derived e deleter punterà ad un distruttore di Derived. Questo dovrebbe funzionare per catena di ereditarietà di qualsiasi lunghezza, ma avere un distruttore virtuale nella classe base è davvero obbligatorio.

consideri prossima semplificata esempio di implementazione ingenuo di tale tecnica:

#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; 
} 

L'output è:

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

distruttori di classe derivata sono chiamati due volte perché ci sono 2 casi di puntatore: uno che era creato in funzione make_pointer e il secondo è in funzione principale.

+0

secondo il vostro i dtr di output sono chiamati due volte, puoi spiegare? – Nik

+0

@Nik si, la risposta è aggiornata. – nikitoz

Problemi correlati