2016-02-09 17 views
5

ho una classe generica che sembra qualcosa di simile:Eliminazione di un modello di tipo

template <class T> 
class Example 
{ 
    private: 
    T data; 
    public: 
    Example(): data(T()) 
    Example(T typeData): data(typeData) 
    ~Example() 

    // ... 
}; 

Sono un po 'confuso su come implementare un decostruttore per qualcosa di simile. In particolare, dal momento che T è di qualsiasi tipo, potrebbe essere allocata memoria nello stack (che è sempre il caso per Example creato tramite il costruttore senza argomenti) o sull'heap.

Per esempio, se il cliente fa il tipo per T un int* e fornisce un puntatore alla memoria dinamica, come faccio a sapere di chiamare delete su data in contrasto con se il client impostare il tipo di int?

+3

Beh, puoi essere come i contenitori standard e non farlo. I distruttori di container standard non faranno nulla agli elementi se il tipo è un tipo di puntatore. – NathanOliver

+0

Suggerimento: utilizzare un idioma del tipo di politica CRTP basato su 'std :: is_pointer '. Divertiti a costruirlo, quindi bin tutto il tutto poiché è * veramente * difficile distinguere i puntatori dalle variabili automatiche da quelle dinamiche nel punto di utilizzo. – Bathsheba

+0

Entrambi i punti validi. Sto lavorando a quello che considererei più un esercizio, quindi alla fine mi stavo chiedendo se mi mancasse qualcosa o se questa fosse solo una cosa difficile da risolvere in generale (che sembra essere il caso) – nmagerko

risposta

7

La risposta più semplice è: no. Non cercare di indovinare l'utente e fare qualcosa che potrebbero non aspettarsi. Adotta lo stesso criterio dei contenitori standard: supponi che T pulisca correttamente da solo.

Se il codice client è scritto correttamente, utilizzerà le classi RAII (come i puntatori intelligenti) per la gestione automatica e corretta della memoria e di altre risorse. Se non lo è, non puoi sperare di risolverlo nel codice del tuo provider.

Fai in modo che la tua classe funzioni con std::unique_ptr e std::shared_ptr, così come qualsiasi altra classe RAII personalizzata, e consenti ai tuoi clienti di gestirli da soli. Cosa succederebbe se volessero archiviare puntatori non proprietari, dopotutto?

+0

Abbastanza corretto - quell'ambiguità di me che gestiva la pulizia rispetto al cliente era davvero ciò di cui ero preoccupato. – nmagerko

2

È possibile utilizzare la specializzazione dei modelli.

template <class T> 
class Example 
{ 
    private: 
    T data; 
    public: 
    Example() 
     : data(T()) 
    {} 

    Example(T typeData): data(typeData) 
    {} 
}; 

template <class T> 
class Example<T*> 
{ 
    private: 
    T* data; 
    public: 
    Example() : data(nullptr){} 
    Example(T* typeData): data(typeData) {} 
    ~Example() 
    { 
     delete data; 
    } 
}; 

int main() 
{ 
    Example<int> e; 
    Example<int*> e2; 

    return 0; 
} 
+1

Modello fresco. Molto più facile documentare il comportamento in questo modo. – nmagerko

+1

Sarei attento a utilizzare questo nel codice di produzione. Come altri hanno già detto: è difficile dire se un puntatore punta o meno sullo spazio di memoria 'newed'. –

0

Non si può semplicemente preoccuparsi di ciò, come fa la libreria standard. Ad esempio, se crei un vettore di puntatori, sei responsabile di eliminarli prima di lasciare che il vettore esca dall'ambito. Le persone possono quindi decidere se vogliono addirittura che vengano cancellate (forse è temporaneo per l'ordinamento e qualcos'altro possiede l'oggetto). Possono anche utilizzare puntatori intelligenti in modo che il vettore distrugga l'oggetto tramite il distruttore per il puntatore intelligente.

In questo caso, less is more. Non devi fare nulla di complicato. Non è necessario mantenere più versioni del modello. Infine, l'utente del tuo modello ha più controllo ... e anche la responsabilità, ovviamente.

0

Ti suggerisco di utilizzare std::unique_ptr per T quando hai bisogno di Example per contenere un puntatore proprietario. Se T è un puntatore non elaborato, semplicemente non lo possiede e non dovrebbe cancellarlo.

Se è necessario Example per inizializzare il puntatore, specializzarlo per std::unique_ptr e chiamare std::make_unique nel costruttore predefinito.

template<typename T> 
class Example<std::unique_ptr<T>> { 
    Example() : data{std::make_unique<T>()} {} 

    /* rest of the class */ 
}; 

Se si esegue questa operazione, non si dovrebbe specializzare la classe per T* di fare un new, come non è possibile inizializzare i puntatori non possedere. Dovresti riceverlo nel tuo costruttore e forse disabilitare il costruttore predefinito per i puntatori raw se non vuoi che sia nullo.

template<typename T> 
class Example<T*> { 
    Example() = delete; 
    Example(T* data_) : data{data_} 

    /* data is not an owning pointer. No need for a destructor */ 

    /* rest of the class */ 
}; 

Se si seguono queste regole, non si dovrebbero avere problemi con la gestione della memoria.

0

Utilizzare classi di template helper di rilascio memoria che possono essere selezionate per tipo. Non è necessario rendere pubblici della classe con la specializzazione dei modelli. È possibile scrivere solo una classe.

Ma è necessario conoscere la forma del nuovo chiamato, quindi utilizzare Elimina o Elimina [].

Problemi correlati