2010-12-11 8 views
12

Estoy seguro de que este código debería ser ilegal, ya que claramente no funcionará, pero parece estar permitido por el C++ 0x FCD.Legalidad de usar el operador delete en un puntero obtenido de la colocación new

class X { /* ... */}; 
void* raw = malloc(sizeof (X)); 
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression 
::operator delete(p); // definitely wrong, per litb's answer 
delete p; // legal? I hope not 

Tal vez uno de sus abogados de idiomas puede explicar cómo lo prohíbe la norma.

Hay también una forma de matriz:

class X { /* ... */}; 
void* raw = malloc(sizeof (X)); 
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression 
::operator delete[](p); // definitely wrong, per litb's answer 
delete [] p; // legal? I hope not 

This is the closest question pude encontrar.

EDIT: yo no voy a comprar el argumento de que los argumentos de la lengua Restricción de la norma para funcionar void ::operator delete(void*) se aplican de una manera significativa al operando de delete en un eliminar la expresión. En el mejor de los casos, la conexión entre los dos es extremadamente tenue, y varias expresiones son permitidas como operandos a delete que no son válidas para pasar a void ::operator delete(void*). Por ejemplo:

struct A 
{ 
    virtual ~A() {} 
}; 

struct B1 : virtual A {}; 

struct B2 : virtual A {}; 

struct B3 : virtual A {}; 

struct D : virtual B1, virtual B2, virtual B3 {}; 

struct E : virtual B3, virtual D {}; 

int main(void) 
{ 
    B3* p = new E(); 
    void* raw = malloc(sizeof (D)); 
    B3* p2 = new (raw) D(); 

    ::operator delete(p); // definitely UB 
    delete p; // definitely legal 

    ::operator delete(p2); // definitely UB 
    delete p2; // ??? 

    return 0; 
} 

espero que esto muestra que si un puntero puede ser pasado a void operator delete(void*) no influye en si mismo puntero puede ser utilizado como el operando de delete.

+1

FYI: El FCD (N3092) ya no es el último borrador. El último borrador es N3225. He mantenido actualizada la [página wiki de la etiqueta C++ - 0x] (http://stackoverflow.com/tags/c%2b%2b0x/info) con un enlace al último borrador del PDF. –

+1

Tenga en cuenta que 5.3.5/2, que cubre esto, se ha modificado en el último borrador. Ahora dice que el puntero puede ser "un puntero a un objeto no array creado por una _new-expression_ previa", y una _new-expression_ incluye incluir nuevas expresiones. No creo que sea así. –

+0

@James: Muchas gracias por el nuevo borrador. Y 5.3.5 es exactamente la sección que creo que debería prohibir esto, pero no es así. ¿Podría mirar mi respuesta (me estoy preparando para seleccionar cualquier cambio de idioma del nuevo borrador) y dejarme saber si cree que tiene alguna relación con esta pregunta? –

Respuesta

2

El compilador no se preocupa realmente de que p proceda de una llamada de colocación new, por lo que no le impedirá emitir delete en el objeto. De esa manera, su ejemplo puede considerarse "legal".

Eso no funcionará, ya que los operadores de "colocación delete" no pueden llamarse explícitamente. Solo se llaman implícitamente si el constructor lanza, por lo que el destructor puede ejecutarse.

2

supongo que podría salirse con la suya (en un compilador particular) si

  1. new/delete se implementan en términos de malloc/free y
  2. la colocación new realmente utiliza el mismo mecanismo para mantener seguimiento del destructor asociado con las asignaciones como el estándar new, para que la llamada al delete pueda encontrar el destructor correcto.

Pero no hay manera de que sea portátil o segura.

+1

Definitivamente no es seguro, porque esa expresión de eliminación es necesaria para llamar a una función de desasignación, pasando un puntero que no proviene de la función de asignación coincidente. –

2

encontré esto en la sección de biblioteca de la norma, que es aproximadamente como contrario a la intuición de una ubicación (OMI) como sea posible:

C++ 0x FCD (y el proyecto de N3225) sección 18.6.1.3, [new.delete.placement] :

Estas funciones están reservados, un programa en C++ puede no definir funciones que desplazan las versiones en la biblioteca de la Norma C++ (17.6.3). Las disposiciones de (3.7.4) no se aplican a estas formas de ubicación reservadas del operador nueva y la eliminación del operador.

void* operator new(std::size_t size, void* ptr) throw(); 
void* operator new[](std::size_t size, void* ptr) throw(); 
void operator delete(void* ptr, void*) throw(); 
void operator delete[](void* ptr, void*) throw(); 

Aún así, la sección que define las expresiones legales para pasar a delete es 5.3.5, 3.7.4 no.

7

las reglas estándar en [basic.stc.dynamic.deallocation] p3

De lo contrario, el valor suministrado a operator delete(void*) en la biblioteca estándar deberá ser uno de los valores devueltos por una invocación anterior de cualquiera de operator new(size_t) o operator new(size_t, const std::nothrow_t&) en la biblioteca estándar, y el valor suministrado a operator delete[](void*) en la biblioteca estándar será uno de los valores devueltos por una invocación previa de operator new[](size_t) o operator new[](size_t, const std::nothrow_t&) en la biblioteca estándar.

Su llamada delete llamará al bibliotecas operator delete(void*), a menos que haya sobrescrito él. Como no has dicho nada al respecto, asumiré que no.

El "debe" de arriba realmente debería ser algo así como "el comportamiento no está definido si no es así", por lo que no se toma por una regla diagnosticable, que no es por [lib.res.on.arguments] p1. Esto fue corregido por n3225 por lo que ya no se puede confundir.

+0

Eso ciertamente se aplica a llamar directamente a las funciones de desasignación usando la sintaxis de llamada a función. Pero no creo que sea inmediatamente aplicable a las invocaciones de 'operator delete', ya que el puntero que utilizo como operando de' operator delete' no es necesariamente el que finalmente se pasa a la función de desasignación. Bueno, leyendo 5.3.4 '[expr.new]' más de cerca, es lo mismo para scalar new. Por lo tanto, piense en términos de colocación de matriz nueva en su lugar, seguida de eliminación de matriz. –

+0

@Ben No entiendo tu comentario. –

+0

Se supone que el compilador es responsable de seguir las reglas para las funciones de desasignación, siempre que siga las reglas para * new-expression * y * delete-expression *. Lo que lamentablemente este código hace, según la redacción actual citada por James. –

Cuestiones relacionadas