Es que haya una buena razón para no declarar un destructor virtual para una clase? Cuándo se debe específicamente evitar escribir uno?

InformationsquelleAutor Mag Roader | 2008-11-19

12 Comentarios

  1. 69

    No es necesario el uso de un destructor virtual cuando ninguna de las siguientes es verdadera:

    • Ninguna intención de derivar las clases de ella
    • No de la creación de instancias en el montón
    • Ninguna intención de almacenar en un puntero de una superclase

    No hay ninguna razón específica para evitarlo, a menos que usted realmente está tan presionado por la memoria.

    • descripción fina. se me cayó mi respuesta en favor de este. +1 🙂
    • Esto no es una buena respuesta. «No es necesario» es diferente de «no debe», y «ninguna intención» es diferente de «imposible».
    • También añadir: ninguna intención de eliminar una instancia a través de un puntero de la clase base.
    • Esto no contesta la pregunta. Dónde está tu buena razón para no usar un virtual dtor?
    • Creo que cuando no hay necesidad de hacer algo, que es una buena razón para no hacerlo. Su siguiente el Simple principio de Diseño de XP.
    • Por que se dice que tiene «ninguna intención», que está haciendo una gran suposición acerca de cómo la clase se acostumbrará. A mí me parece la solución más simple en la mayoría de los casos (por defecto) debe ser destructores virtuales, y sólo si tiene una razón específica para no hacerlo. Así que todavía estoy curioso acerca de lo que sería una buena razón.
    • No relacionados con el montón exactamente, pero más a léxica variable de alcance. Cualquier valor de la variable, cuando el objeto de una clase, es bien conocido en tiempo de compilación, por lo que no puede haber ninguna confusión sobre lo que el destructor de la llamada. Los valores de las variables locales son exactamente los valores que se guardan en la pila, no el montón; por lo tanto, sólo asigna el montón de objetos deben tener cuidado de que el destructor se llama.
    • Tan cierto! Cuántos de nosotros hemos hecho el error de novato de la herencia de std::vector? Casi siempre esta es una idea terrible, pero perfectamente encapsula «no debe» vs «no se puede / hecho imposible».
    • Por favor me corrija si estoy equivocado, pero se dice que cuando cualquier condición se aplica, pero: No de la creación de instancias en el montón , usted puede tener un subclase con la dinámica de la memoria y el código siguiente se fuga de recursos: Base *b = new Derived(); delete b; ver
    • asignación de nuevo es la asignación en el montón.

  2. 67

    Para responder a la pregunta de forma explícita, es decir, cuándo se debe no declarar un destructor virtual.

    C++ ’98/’03

    La adición de un destructor virtual podría cambiar su clase de POD (plain old datos)* o agregado a la no-POD. Esto puede detener el proyecto de compilación de si la clase es de tipo agregado inicializado en algún lugar.

    struct A {
      //virtual ~A ();
      int i;
      int j;
    };
    void foo () { 
      A a = { 0, 1 };  //Will fail if virtual dtor declared
    }

    En un caso extremo, un cambio también puede causar un comportamiento indefinido donde la clase está siendo utilizado de una manera que requiere de un POD, por ejemplo, la que pasa a través de una elipsis parámetro, o usarlo con memcpy.

    void bar (...);
    void foo (A & a) { 
      bar (a);  //Undefined behavior if virtual dtor declared
    }

    [* Un tipo de POD es un tipo que ha garantías específicas acerca de su diseño de memoria. El estándar realmente sólo dice que si se va a copiar de un objeto con el tipo de POD en un array de caracteres (o caracteres sin signo) y de nuevo, entonces el resultado será el mismo que el del objeto original.]

    C++ Moderno

    En las últimas versiones de C++, el concepto de la VAINA fue dividida entre la clase de diseño y su construcción, la copia y la destrucción.

    De los puntos suspensivos caso, no es un comportamiento indefinido es ahora condicionalmente-compatible con la aplicación definida por la semántica (N3937 – ~C++ ’14 – 5.2.2/7):

    …Pasar un potencialmente evaluado argumento de tipo de clase (Cláusula 9) tener un no-trivial constructor de copia, no trivial de mover constructor, o un trivial destructor, que no correspondan a ningún parámetro, es condicionalmente-compatible con la aplicación-semántica definida.

    Declarar un destructor otros que =default va a decir que no trivial (12.4/5)

    … Un destructor es trivial si no es proporcionado por el usuario …

    Otros cambios en C++ Moderno reducir el impacto de los agregados problema de inicialización como un constructor puede ser añadido:

    struct A {
      A(int i, int j);
      virtual ~A ();
      int i;
    
      int j;
    };
    void foo () { 
      A a = { 0, 1 };  //OK
    }
    • Tienes razón, y yo estaba equivocado, el rendimiento no es la única razón. Pero esto demuestra que yo tenía razón sobre el resto: la clase del programador tenía mejor incluir código para evitar que la clase de nunca ser heredado por nadie más.
    • estimado Richard, por favor, puedes comentar un poco más sobre lo que usted ha escrito. No entiendo tu punto, pero parece que el único valioso que he encontrado por google) O puede ser que usted puede dar un enlace a una explicación más detallada?
    • He actualizado la respuesta. Espero que esta ayuda.
  3. 25

    Declaro un destructor virtual si y sólo si tengo a los métodos virtuales. Una vez que he métodos virtuales, yo no confío en mí mismo para evitar la creación de instancias en el montón o almacenar un puntero a la clase base. Ambos son extremadamente comunes de las operaciones y a menudo la pérdida de recursos en silencio si el destructor no es declarado como virtual.

    • Y, de hecho, hay una opción de advertencia en gcc que advierte, precisamente, sobre ese caso (métodos virtuales pero no virtual dtor).
    • No, a continuación, ejecutar el riesgo de fugas de memoria si se derivan de la clase, independientemente de si usted tiene otras funciones virtuales?
    • Estoy de acuerdo con el mag. Este uso de un destructor virtual y virtual o de forma independiente los requisitos. Destructor Virtual proporciona la capacidad de una clase para realizar la limpieza (por ejemplo, borrar la memoria, cerrar archivos, etc…) Y también asegura que los constructores de todos sus miembros, se llama.
  4. 7

    Un destructor virtual es necesario siempre existe la posibilidad de que delete podría ser llamado un puntero a un objeto de una subclase con el tipo de su clase. Esto asegura la correcta destructor se llama en tiempo de ejecución sin que el compilador tener que saber la clase de un objeto en la pila en tiempo de compilación. Por ejemplo, suponga B es una subclase de A:

    A *x = new B;
    delete x;     //~B() called, even though x has type A*

    Si el código no es de rendimiento crítico, sería razonable para agregar un destructor virtual para cada clase de base de escribir, sólo por seguridad.

    Sin embargo, si usted se encuentra deleteing una gran cantidad de objetos en un bucle estrecho, la sobrecarga de rendimiento de llamar a una función virtual (incluso uno que está vacía) puede ser notable. El compilador no puede generalmente en línea de estas llamadas, y el procesador puede tener un tiempo difícil predecir a dónde ir. Es poco probable que esto tendría un impacto significativo en el rendimiento, pero vale la pena mencionar.

    • «Si el código no es de rendimiento crítico, sería razonable para agregar un destructor virtual para cada clase de base de escribir, sólo por seguridad.» debe ser enfatizado más en cada respuesta que veo
  5. 5

    Funciones virtuales decir que cada asignados objeto de aumentos en los costes de memoria por una función virtual puntero a la tabla.

    Así que si su programa implica la asignación de un número muy grande de algún objeto, sería vale la pena evitar todas las funciones virtuales con el fin de guardar los 32 bits adicionales por objeto.

    En todos los demás casos, usted se ahorrará de depuración de la miseria a hacer el dtor virtual.

    • Sólo nit-picking, pero en estos días un puntero a menudo serán de 64 bits en lugar de 32.
  6. 5

    No todas las clases de C++ son adecuados para su uso como una clase base con la dinámica de polimorfismo.

    Si quieres que tu clase sea adecuado para la dinámica de polimorfismo, a continuación, su destructor debe ser virtual. Además, cualquiera de los métodos que una subclase podría desee anular (lo cual podría significar que todos los métodos públicos, además de potencialmente protegidas de los que se usan internamente) debe ser virtual.

    Si la clase no es adecuado para la dinámica de polimorfismo, a continuación, el destructor no debe ser marcada virtual, porque para hacerlo es engañosa. Sólo se anima a la gente a usar su clase incorrectamente.

    He aquí un ejemplo de una clase que no sería conveniente para la dinámica de polimorfismo, incluso si su destructor fueron virtual:

    class MutexLock {
        mutex *mtx_;
    public:
        explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
        ~MutexLock() { mtx_->unlock(); }
    private:
        MutexLock(const MutexLock &rhs);
        MutexLock &operator=(const MutexLock &rhs);
    };

    El punto entero de esta clase es sentarse en la pila para RAII. Si usted está pasando alrededor de punteros a objetos de esta clase, y no digamos las subclases de ella, entonces Lo estás Haciendo Mal.

    • Polimórficos uso no implica polimórficos eliminación. Hay un montón de casos de uso para una clase virtual métodos aún no destructor virtual. Considere la posibilidad de un típico definido estáticamente cuadro de diálogo, en casi cualquier conjunto de herramientas GUI. El padre se abrirá la ventana de destruir el niño de los objetos, y no conoce el tipo exacto de cada uno, sin embargo, todas las ventanas secundarias se utilizará también polymorphically en cualquier número de lugares, tales como el golpe de pruebas, el dibujo, la Api de accesibilidad que alcanzan el texto para texto-a-voz, motores, etc.
    • Cierto, pero el interlocutor pregunta cuando específicamente, debe evitar un destructor virtual. Para el cuadro de diálogo que usted describe, un destructor virtual es inútil, pero IMO no es perjudicial. No estoy seguro de que me gustaría estar seguro de que nunca voy a necesitar para eliminar un cuadro de diálogo con un puntero de la clase base – por ejemplo, yo en el futuro, quiero que mi padre ventana para crear sus objetos secundarios del uso de las fábricas. Así que no es una cuestión de evitando destructor virtual, sólo que quizás no quieres tener uno. Un destructor virtual en una clase no es adecuado para la derivación de es perjudicial, sin embargo, porque es engañoso.
  7. 4

    Una buena razón para no declarar un destructor virtual es cuando esto guarda su clase de contar con una tabla de funciones virtuales añadido, y se debe evitar que, siempre que sea posible.

    Sé que muchas personas prefieren acaba de declarar siempre destructores como virtual, sólo para estar en el lado seguro. Pero si la clase no tiene ningún otro virtual de funciones, entonces no hay realmente ninguna razón para tener un destructor virtual. Incluso si usted da su clase a otras personas que a continuación se derivan de otras clases a partir de entonces no tendría razón para llamar nunca a borrar un puntero que se upcast a su clase – y si lo hacen, entonces yo consideraría esto un error.

    Bueno, hay una sola excepción, a saber, si la clase es (mal)utilizado para realizar polimórficos eliminación de objetos derivados, pero entonces usted – o los otros chicos – esperemos que saber que esto requiere de un destructor virtual.

    Dicho de otra manera, si la clase tiene un no-destructor virtual, a continuación, esta es una declaración muy clara: «no me utilizan para la eliminación de objetos derivados!»

  8. 3

    Si usted tiene una muy pequeña clase con un gran número de casos, la sobrecarga de un puntero vtable puede hacer una diferencia en su uso de memoria del programa. Mientras que su clase no tiene otros métodos virtuales, haciendo que el destructor no virtual va a guardar esa sobrecarga.

  9. 1

    Me suele declarar el destructor virtual, pero si usted tiene rendimiento crítico código que se utiliza en un bucle interno, es posible que desee evitar la tabla virtual de búsqueda. Que puede ser importante en algunos casos, como la comprobación de colisiones. Pero ser cuidadoso acerca de cómo destruir a los objetos si utilizar la herencia, o que va a destruir sólo la mitad del objeto.

    Tenga en cuenta que la tabla virtual de búsqueda que sucede para un objeto si cualquier método en el objeto virtual. Así que no hay punto en la eliminación virtual de la especificación de un destructor si usted tiene otros métodos virtuales en la clase.

  10. 1

    Si estoy absolutamente debe asegurarse de que su clase no tiene una vtable, a continuación, usted no debe tener un destructor virtual así.

    Este es un caso raro, pero no es así.

    El ejemplo más conocido de un patrón que hace esto son los DirectX D3DVECTOR y D3DMATRIX clases. Estos son los métodos de la clase en lugar de funciones para el azúcar sintáctico, pero las clases intencionalmente no tienen una vtable para evitar que la función de gastos, ya que estas clases se utilizan específicamente en el bucle interno de muchas aplicaciones de alto rendimiento.

  11. 0

    De la operación que se llevará a cabo en la clase base, y que debe comportarse prácticamente, debe ser virtual. Si la eliminación se puede realizar polymorphically a través de la interfaz de clase base, entonces debe comportarse de forma virtual y de ser virtual.

    El destructor no tiene ninguna necesidad de ser virtual si no va a derivar de la clase. E incluso si lo hace, protegida no destructor virtual es igual de buena si la supresión de los punteros de la clase base no requiere.

  12. -7

    El rendimiento de respuesta es el único que sabe de que representa una oportunidad de ser cierto. Si usted ha medido y se encontró que de la virtualización de su destructores realmente acelera las cosas, es probable que usted haya otras cosas en la clase que necesita acelerar demasiado, pero en este punto hay consideraciones más importantes. Algún día alguien va a descubrir que su código será proporcionar una buena base de la clase para ellos y salvarlos de una semana de trabajo. Es mejor asegurarse de que hacer que el trabajo de una semana, copiar y pegar el código, en lugar de utilizar el código como base. Es mejor asegurarse de que algunos de sus métodos importantes privado, de modo que nadie puede heredar de ti.

    • Polimorfismo ciertamente las cosas más despacio. Lo compara con la situación en la que tenemos polimorfismo y elegir no será aún más lento. Ejemplo: nos implementar toda la lógica en la base destructor de la clase, utilizando RTTI y una instrucción switch para limpiar los recursos.
    • En C++, no es su responsabilidad para detener el me heredar de las clases que hemos documentado no son adecuados para su uso como base de las clases. Es mi responsabilidad el uso de la herencia con precaución. A menos que el estilo de casa de guía dice lo contrario, por supuesto.
    • … acabo de hacer el destructor virtual no significa que la clase tendrá necesariamente que funcione correctamente, como una clase base. Así marcando virtual «porque sí», en lugar de hacer que la evaluación, es escribir un cheque mi código no puede en efectivo.

Dejar respuesta

Please enter your comment!
Please enter your name here