2011-11-24 8 views
7

Considere el programa a continuación. Se ha simplificado a partir de un caso complejo. No puede borrar la memoria asignada anterior, a menos que elimine el destructor virtual en la clase Obj. No entiendo por qué las dos direcciones de la salida del programa son diferentes, solo si el destructor virtual está presente.¿Qué hay de malo en este uso de la ubicación nueva []? do

// GCC 4.4 
#include <iostream> 

using namespace std; 

class Arena { 
public: 
    void* alloc(size_t s) { 
     char* p = new char[s]; 
     cout << "Allocated memory address starts at: " << (void*)p << '\n'; 
     return p; 
    } 

    void free(void* p) { 
     cout << "The memory to be deallocated starts at: " << p << '\n'; 
     delete [] static_cast<char*> (p); // the program fails here 
    } 
}; 

struct Obj { 
    void* operator new[](size_t s, Arena& a) { 
     return a.alloc(s); 
    } 

    virtual ~Obj() {} // if I remove this everything works as expected 

    void destroy(size_t n, Arena* a) { 
     for (size_t i = 0; i < n; i++) 
      this[n - i - 1].~Obj(); 
     if (a) 
      a->free(this); 
    } 
}; 


int main(int argc, char** argv) { 
    Arena a; 

    Obj* p = new(a) Obj[5](); 
    p->destroy(5, &a); 

    return 0; 
} 

Ésta es la salida del programa en mi aplicación cuando el destructor virtual está presente:

dirección de memoria asignada comienza a las: 0x8895008 La memoria para cancelar la asignación comienza a: 0x889500c

RUN FAILED (valor de salida 1)

Por favor, no pregunte qué programa se supone que debe d o Como dije, proviene de un caso más complejo en el que Arena es una interfaz para varios tipos de memoria. En este ejemplo, la memoria solo se asigna y desasigna del montón.

+0

Tenga en cuenta que haces * * necesita una matriz ubicación coincidentes borrar en caso de una nueva expresión se produce una excepción. –

Respuesta

5

this no es el puntero devuelto por el new en la línea char* p = new char[s]; Se puede ver que el tamaño s no es más grande que 5 Obj casos. La diferencia (que debe ser sizeof (std::size_t)) está en la memoria adicional, que contiene la longitud de la matriz, 5, inmediatamente antes de la dirección contenida en this.

bien, la especificación deja claro:

http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies

2,7 operador matriz de nuevas cookies

Cuando se utiliza el operador new para crear una nueva matriz, una cookie se almacena generalmente para recordar la longitud asignada (número de elementos de la matriz) para que pueda desasignarse correctamente.

Específicamente:

No se requiere cookie si el tipo de elemento de la matriz T tiene un destructor trivial (12,4 [class.dtor]) y el (array) función desasignación habitual (3.7.3.2 [basic.stc .dynamic.deallocation]) la función no toma dos argumentos.

Así, el virtuales -ness del destructor es irrelevante, lo importante es que el destructor no es trivial, que se puede comprobar fácilmente, mediante la supresión de la palabra clave virtual frente al destructor y Observe el programa bloqueándose.

+0

Sí, realmente no me importa el tamaño. Me importa la razón por la que esto no coincide con el puntero que alloc() ha dado para colocar el objeto. – Martin

+0

@Martin, porque 'alloc()' ha asignado espacio adicional para la longitud de la matriz y siguen los elementos de la matriz. Realmente no sé ahora por qué no almacena la longitud de la matriz si no hay un destructor virtual, tengo que verificar la especificación de C++ ABI. – chill

+0

¿Qué especificaciones es esto? Mi especificación C++ no menciona las cookies. –

0

Basado en respuesta escalofríos, si quieres que sea 'seguro':

#include <type_traits> 

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0)); 
+1

No, no realmente, tal vez 'a-> free ((std :: size_t *) this - (std :: has_trivial_destructor :: value? 1: 0));', pero estrictamente hablando UD y estoy seguro se romperá en algún oscuro caso de esquina y/o compilador diferente;) – chill

Cuestiones relacionadas