2010-02-16 10 views
13

Encontré this one question asking the same thing, sin embargo, solo se respondió la parte 'nueva', así que aquí va otra vez.¿Por qué se requiere que el operador de eliminación sea estático?

¿Por qué se requiere que el operador de eliminación sea estático? De alguna manera no tiene sentido. El nuevo operador tiene mucho sentido, al igual que el constructor no puede ser virtual, como tampoco el nuevo operador. Sin embargo, el destructor puede (y debería) ser virtual cuando se usa la herencia, para permitir la destrucción de los objetos utilizados (por medio de polimorfismo) como una clase base.

Entiendo que, cuando se llama al operador delete, el objeto ya se ha destruido, por lo que no existe 'esto'. Sin embargo, sigue teniendo sentido utilizar el mismo razonamiento que con el destructor virtual para que el operador de eliminación coincida con el nuevo operador que creó el objeto.

Esto es lo que quiero decir

class A 
{ 
    public: 
    virtual ~A() {} 
}; 

class B : public A 
{ 
    public: 
    void* operator new (size_t sz); 
    void operator delete (void* ptr, size_t sz); 
}; 

ahora si hacemos operador delete

A *ptr = new B(); 
delete ptr; // <-- fail 

de A (por defecto) debería haber sido llamados, ya que es estático y no se sabe (por cualquier cosa menos el caso trivial aquí) en tiempo de compilación, que delete-operator es el correcto.

Sin embargo, hice un pequeño programa de prueba con el código anterior (solo malloc/free en los operadores new/delete e print statement en delete) y lo compilé usando g ++. Ejecutarlo de manera inesperada produjo el resultado en el operador de eliminación de B.

Mi pregunta (real) es esta: ¿hay alguna 'virtualidad' implícita para el operador de eliminación? ¿Es solo estático en el sentido de no-puntero? ¿O es solo una característica de g ++?

Comencé a buscar a través de la especificación C++, pero debo admitir que me sentí abrumado por ella, por lo que cualquier ayuda apreciada.

Respuesta

16

La respuesta en las reglas de idioma está realmente en 12.5 [class.free].

Si está eliminando mediante un puntero a una clase base, entonces el destructor debe ser virtual o se obtendrá un comportamiento indefinido. De lo contrario, la implementación debe determinar el tipo dinámico del objeto que se va a eliminar.

12,5/4 dice que cuando el delete no es prefijado por :: entonces la función de cancelación de asignación se determina por buscar delete en el contexto del destructor virtual de tipo dinámico. Esto garantiza la búsqueda virtual, aunque operator delete es siempre una función de miembro static.

asignación Raw y desasignación suceden conceptualmente fuera de la vida del objeto de manera que cuando la función de cancelación de asignación es de ser llamado, ya no es un objeto proporcionar un mecanismo de búsqueda virtual, pero las reglas de búsqueda asegurar que operator delete tiene una dinámica (mecanismo de búsqueda virtual-lite!). Esto significa que la eliminación del operador puede ser sensiblemente static sin perder el contacto con el tipo dinámico del objeto original.

+0

Excelente, gracias! Estuve hurgando en 3.7.3 ... – falstro

+3

También hay material relevante en 5.3.5 y 3.7.3 y 12.5. Aunque se considera una obra de referencia, parece que tienes que leerlo de principio a fin, de lo contrario nunca sabrías si hay un pequeño párrafo relevante para algo que estás buscando en una sección completamente diferente de donde eras en realidad mirando. –

1

delete operador es para desasignar la memoria solamente, y la memoria es desasignada para el objeto de clase más derivado como un todo - en una acción - exactamente de la misma manera que con el operador new se asigna para el objeto de clase más derivado - el objeto de clase pasado como argumento en new Class constructo.

Es por esto que cuando hace delete ptr;, el operador delete siempre se llama solo una vez para la clase real más derivada del objeto que se está eliminando y los datos sobre qué clase se deducen del archivo vtable si el destructor virtual es presente o el tipo del puntero si no hay destructor virtual. Es por eso que no hay virtualidad implícita para el operador delete: toda virtualidad finaliza en el punto de la llamada al destructor.

+0

@roe: la eliminación del operador de clase más derivada se llama de la misma manera que se llama al operador más nuevo que se llama nuevo. Entonces, si tiene bancos diferentes, obtendrá objetos de diferentes clases asignados de diferentes bancos, pero solo como objetos completos, nunca sucederá que un subobjeto sea de un banco y que la parte de clase derivada del objeto sea de otro. – sharptooth

+0

Entonces, el operador que se usa se deduce de qué clase es el objeto real (es decir, busca una tabla virtual, o como sea que funcione), parece que hay cierta virtualidad implícita para el operador? Suponiendo que tengo una clase C derivada de B, 'A * ptr = new C; delete ptr; 'todavía llama al operador de eliminación de B, lo que suena como que es prácticamente virtual para mí. Perdón por el comentario spam, solo estoy tratando de entenderlo. – falstro

+0

@roe: eliminar y los nuevos operadores se heredan. Ya que los sobrecargó en clase B clase C también los usará. Vea la bonita tabla aquí: http://msdn.microsoft.com/en-us/library/c5at8eya.aspx – sharptooth

Cuestiones relacionadas