2009-10-23 12 views
9

¿Cómo es posible que se permita eliminar objetos con destructor privado en el siguiente código? Reduje el programa real a la siguiente muestra, pero aún compila y funciona.Eliminación de objetos con destructor privado

class SomeClass; 

int main(int argc, char *argv[]) 
{ 
    SomeClass* boo = 0; // in real program it will be valid pointer 
    delete boo; // how it can work? 

    return -1; 
} 

class SomeClass 
{ 
private: 
    ~SomeClass() {}; // ! private destructor ! 
}; 
+0

Interesting..if muevo la definición de la clase anterior main() entonces se lanza un error de compilación. De lo contrario, acabo de recibir una 'advertencia C4150: eliminación del puntero al tipo incompleto 'SomeClass'; no hay destructor llamado' – Naveen

+1

@Naveen: Eso es lo esperado. El tipo incompleto es un problema. El destructor privado es otro. Cada uno tiene su propio mensaje de diagnóstico. Cambia entre los dos moviendo la definición de 'SomeClass'. – AnT

Respuesta

15

Usted está intentando eliminar objeto de tipo clase incompleta. C++ estándar dice que obtendrá un comportamiento indefinido en este caso (5.3.5/5):

Si el objeto se va a eliminar tiene tipo de clase incompleta en el punto de eliminación y la clase completa tiene un no trivial destructor o una función de desasignación, el comportamiento no está definido.

para detectar tales casos se puede utilizar boost::checked_delete:

template<typename T> 
inline void checked_delete(T* p) 
{ 
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; 
    (void) sizeof(type_must_be_complete); 
    delete p; 
} 
+1

Esto toma ventaja de sizeof() nunca devuelve 0 incluso para estructuras/clases vacías, que había olvidado. ISO-IEC-14882 sección 9.1: "Los objetos completos y los objetos secundarios del tipo de clase tendrán un tamaño distinto de cero". La implicación es que cada objeto en una matriz tendrá una dirección de memoria única. Probablemente lo mismo para las estructuras, ¿es eso también en C? – leander

+0

En el tamaño C de la estructura vacía es igual a 0. –

+0

@leander: No exactamente. Usted ve en C++ que es simplemente * ilegal * aplicar 'sizeof' a un tipo incompleto. Esto es lo que el código anterior está tratando de atrapar por la parte '(void) sizeof ...'. Esta expresión detendrá la compilación en un compilador que cumpla con la norma al emitir un * error *. Sin embargo, si un compilador extraño permite '' sizeof' en tipos incompletos (como una extensión) con 0 resultado, entonces la parte 'typedef ...' los detectará y forzará la falla de "una matriz de tamaño negativo". Entonces, tienes una "trampa" principal aquí (la parte '(void)') y una trampa auxiliar "por las dudas" (la parte 'typedef'). – AnT

7

Este código provoca un comportamiento indefinido (UB). Es UB en C++ a delete un objeto de tipo incompleto que tiene un destructor no trivial. Y en su código el tipo SomeClass está incompleto en el punto delete, y tiene un destructor no trivial. Los compiladores suelen emitir una advertencia al respecto, ya que formalmente en C++ esto no es una violación de restricción.

Por lo tanto, estrictamente hablando, su código no "funciona". Simplemente compila y hace algo undefined cuando se ejecuta.

El compilador simplemente no es necesario para detectar este error. La razón de esto es que esto podría estar perfectamente bien si su objeto tiene un trivial destructor. El compilador no tiene forma de saber qué clase de destructor tendrá este tipo, por lo que no puede decir con certeza si se trata de un error o no.

+0

Estaría bien si (1) 'T' tiene un destructor trivial (2)' T' no redefine 'delete operator'' – curiousguy

4

Porque el tipo SomeClass no está completamente declarado al invocar operator delete.

Eliminar dicho puntero es un comportamiento indefinido, pero en la práctica la mayoría de los compiladores simplemente liberarían la memoria (si el puntero no era NULL) y no llamarían al destructor.

Por ejemplo, g ++ le dará una advertencia acerca de este tema:

foo.cpp: In function 'int main(int, char**)': 
foo.cpp:6: warning: possible problem detected in invocation of delete operator: 
foo.cpp:5: warning: 'boo' has incomplete type 
foo.cpp:1: warning: forward declaration of 'struct SomeClass' 
foo.cpp:6: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined. 
+1

" Eliminar dicho puntero simplemente liberaría la memoria ... "- ¡NO! El comportamiento es * INDEFINIDO *, es decir, cualquier suposición sobre lo que "esencialmente" sucede no es válida. – DevSolar

+1

@DevSolar: Es cierto, no está definido según el estándar. En la práctica, eso es lo que sucederá en la mayoría de los compiladores. He actualizado la respuesta para reflejar esto. – laalto

+0

Tengo un problema para confiar en algo que esté fuera del dominio del buen comportamiento. Especialmente en el código de compañeros de trabajo. Me saca de quicio. Culpo a respuestas como la tuya. (;-) << - smiley importante) – DevSolar

Cuestiones relacionadas