2012-04-03 11 views
6

Me gustaría saber si necesito escribir destructor en clases cuando ya no uso punteros crudos? Simplemente aumente los indicadores inteligentes.Smart pointers & destructor

+0

Tal @simchona sabe algo que yo no, pero punteros inteligentes, no alivian la necesidad de la limpieza, que acaba de cambiar la forma (cuando) se produce. –

+2

Estoy leyendo esta pregunta como, ¿aliméntelos y elimine la necesidad de escribir destructores en casi todos los casos? ¿Es eso correcto? – gymbrall

+0

@ Sí, si tiene un puntero_profundo (o como se llame), entonces no necesita asignación/mover/destructor/constructores. (Aunque los constructores todavía pueden ser útiles a veces) –

Respuesta

-2

Siempre debe considerar proporcionar un destructor. Lo usarías para liberar los recursos que tu clase tenga. A menudo, mis destructores de la clase smart_ptr están vacíos, pero no siempre. Las secuencias de archivos, las conexiones de bases de datos, etc. necesitan una limpieza adecuada.

+0

Todas esas cosas se pueden limpiar envolviéndolas en una clase RAII, como un 'smart_ptr'. Con C++ 11, los destructores de clase deberían ser raros. –

+1

@Mooing No es broma. Los punteros inteligentes/RAII reducen mucho mi código y mis destructores generalmente están vacíos. Se toman un tiempo para acostumbrarse, pero vale la pena aprenderlo con seguridad. – Aura

10

Boost punteros inteligentes por sí mismos no tienen nada que ver con la necesidad de un destructor. Todo lo que hacen es eliminar la necesidad de llamar a eliminar en la memoria asignada que están administrando de manera efectiva. Habiendo dicho eso, si antes de comenzar a usar punteros inteligentes todo lo que tenía en sus destructores eran llamadas para eliminar y eliminar [] liberando la memoria de los miembros de la clase asignados dinámicamente y ahora ha cambiado todos esos punteros a punteros inteligentes, podría probablemente solo cambien a un destructor vacío ya que ahora se limpiarán por sí mismos cuando salgan del alcance.

Sin embargo, si por alguna razón, tiene una clase que necesita limpieza (limpieza de archivos, sockets, otros recursos, etc.), deberá proporcionar un destructor para hacerlo.

Avísame si eso ayuda.

+0

Todas esas cosas se pueden limpiar envolviéndolas en una clase RAII, como un 'smart_ptr'. Con C++ 11, los destructores de clase deberían ser raros. –

4

Cada tipo de recurso debe tener una clase RAII para administrar ese recurso. Si también tiene un puntero inteligente con semántica de copia profunda (bastante fácil de hacer), eso es todo lo que necesita para administrar sus recursos el 99.9% del tiempo. No sé por qué unique_ptr no hace copias en profundidad, ni ningún puntero inteligente de refuerzo, pero si tiene esas dos cosas, no necesita escribir constructores de copia, mover constructores, operadores de asignación, operadores de asignación de movimiento, ni destructores. Puede o no tener que proporcionar otros constructores (incluido el constructor predeterminado), pero esos son cinco lugares menos para cometer errores.

#include <memory> 

template<class Type, class Del = std::default_delete<Type> > 
class deep_ptr : public std::unique_ptr<Type, Del> { 
public: 
    typedef std::unique_ptr<Type, Del> base; 
    typedef typename base::element_type element_type; 
    typedef typename base::deleter_type deleter_type; 
    typedef typename base::pointer pointer; 

    deep_ptr() : base() {} 
    //deep_ptr(std::nullptr_t p) : base(p) {} //GCC no has nullptr_t? 
    explicit deep_ptr(pointer p) : base() {} 
    deep_ptr(pointer p, const typename std::remove_reference<Del>::type &d) : base(p, d) {} //I faked this, it isn't quite right 
    deep_ptr(pointer p, typename std::remove_reference<Del>::type&& d): base(p, d) {} 
    deep_ptr(const deep_ptr& rhs) : base(new Type(*rhs)) {} 
    template<class Type2, class Del2> 
    deep_ptr(const deep_ptr<Type2, Del2>& rhs) : base(new Type(*rhs)) {} 
    deep_ptr(deep_ptr&& rhs) : base(std::move(rhs)) {} 
    template<class Type2, class Del2> 
    deep_ptr(deep_ptr<Type2, Del2>&& rhs) : base(std::move(rhs)) {} 

    deep_ptr& operator=(const deep_ptr& rhs) {base::reset(new Type(*rhs)); return *this;} 
    template<class Type2, class Del2> 
    deep_ptr& operator=(const deep_ptr<Type2, Del2>& rhs) {base::reset(new Type(*rhs)); return *this;} 
    deep_ptr& operator=(deep_ptr&& rhs) {base::reset(rhs.release()); return *this;} 
    template<class Type2, class Del2> 
    deep_ptr& operator=(deep_ptr<Type2, Del2>&& rhs) {base::reset(rhs.release()); return *this;} 
    void swap(deep_ptr& rhs) {base::swap(rhs.ptr);} 
    friend void swap(deep_ptr& lhs, deep_ptr& rhs) {lhs.swap(rhs.ptr);} 
}; 

Con esta clase (o una similar), ¡no necesita mucho!

struct dog { 
    deep_ptr<std::string> name; 
}; 

int main() { 
    dog first; //default construct a dog 
    first.name.reset(new std::string("Fred")); 
    dog second(first); //copy construct a dog 
    std::cout << *first.name << ' ' << *second.name << '\n'; 
    second.name->at(3) = 'o'; 
    std::cout << *first.name << ' ' << *second.name << '\n'; 
    second = first; //assign a dog 
    std::cout << *first.name << ' ' << *second.name << '\n'; 
} 

Como se ha demostrado en http://ideone.com/Kdhj8