Pregunta

Digamos que tengo el siguiente C++:

char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;

¿Es esto seguro según el estándar C++?¿Necesito volver a un char* y luego usar delete[]?Sé que funcionará en la mayoría de los compiladores de C++, porque son datos comunes y corrientes, sin destructores.¿Se garantiza que será seguro?

¿Fue útil?

Solución

No se garantiza que sea seguro.Aquí hay un enlace relevante en las preguntas frecuentes de C++ lite:

[16.13] ¿Puedo dejar el [] al eliminar una matriz de algún tipo incorporado (char, int, etc.)?

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13

Otros consejos

No, es un comportamiento indefinido: un compilador podría hacer algo diferente y, como dice la entrada de preguntas frecuentes de C++, ruido sordo vinculado a dice, operator delete[] Podría estar sobrecargado para hacer algo diferente. operator delete.A veces puedes salirte con la tuya, pero también es una buena práctica acostumbrarte a hacer coincidir eliminar[] con nuevo[] en los casos en los que no puedas.

Lo dudo mucho.

Hay muchas formas cuestionables de liberar memoria, por ejemplo puedes usar delete en tu char matriz (en lugar de delete[]) y probablemente funcionará bien.I blogueado en detalle sobre esto (disculpas por el autovínculo, pero es más fácil que reescribirlo todo).

El compilador no es tanto el problema sino la plataforma.La mayoría de las bibliotecas utilizarán los métodos de asignación del sistema operativo subyacente, lo que significa que el mismo código podría comportarse de manera diferente en Mac o en Mac.Windows vs.Linux.He visto ejemplos de esto y todos eran códigos cuestionables.

El enfoque más seguro es asignar y liberar memoria siempre utilizando el mismo tipo de datos.Si estás asignando chars y devolverlos a otro código, es mejor que proporciones métodos específicos de asignación/desasignación:

SOME_STRUCT* Allocate()
{
    size_t cb; // Initialised to something
    return (SOME_STRUCT*)(new char[cb]);
}

 

void Free(SOME_STRUCT* obj)
{
    delete[] (char*)obj;
}

(Sobrecargar el new y delete Los operadores también pueden ser una opción, pero nunca me ha gustado hacer esto).

El estándar C++ [5.3.5.2] declara:

Si el operando tiene un tipo de clase, el operando se convierte en un tipo de puntero llamando a la función de conversión mencionada anteriormente, y el operando convertido se usa en lugar del operando original para el resto de esta sección.En cualquier alternativa, el valor del operando de eliminación puede ser un valor de puntero nulo. Si no es un valor de puntero nulo, en la primera alternativa (objeto Eliminar), el valor del operando de delete será un puntero a un objeto no matriz o un puntero a un subobjeto (1.8) que represente una clase base de tales dichos un objeto (cláusula 10).Si no, el comportamiento no está definido.En la segunda alternativa (matriz de eliminación), el valor del operando de eliminación será el valor del puntero que resulta de una nueva expresión de matriz anterior.77) Si no, el comportamiento no está definido.[ Nota:Esto significa que la sintaxis de la expresión de eliminación debe coincidir con el tipo de objeto asignado por nuevo, no la sintaxis de la nueva expresión.—nota final] [ Nota:Un puntero a un tipo de const puede ser el operando de una expresión de eliminación;No es necesario arrojar la Constura (5.2.11) de la expresión del puntero antes de que se use como operando de la expresión deleTe.—nota final]

Esta es una pregunta muy similar a la que respondí aquí: Texto del enlace

En resumen, no, no es seguro según el estándar C++.Si, por alguna razón, necesita un objeto SOME_STRUCT asignado en un área de memoria que tiene una diferencia de tamaño con respecto a size_of(SOME_STRUCT) (¡y será mejor que sea más grande!), entonces será mejor que utilices una función de asignación sin procesar como global operator new para realizar la asignación y luego crear la instancia del objeto en la memoria sin formato con una ubicación new.Colocación new Será extremadamente económico si el tipo de objeto no tiene constructor.

void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;

// ...

delete pSS;

Esto funcionará la mayor parte del tiempo.Siempre debería funcionar si SOME_STRUCT es una estructura POD.También funcionará en otros casos si SOME_STRUCTEl constructor de no lanza y si SOME_STRUCT no tiene un operador personalizado de eliminación.Esta técnica también elimina la necesidad de yesos.

::operator new y ::operator delete son el equivalente más cercano de C++ a malloc y free y como estos (en ausencia de anulaciones de clase) son llamados según corresponda por new y delete expresiones que pueden (¡con cuidado!) usarse en combinación.

Mientras esto debería funciona, no creo que puedas garantizar que sea seguro porque SOME_STRUCT no es un char* (a menos que sea simplemente un typedef).

Además, dado que está utilizando diferentes tipos de referencias, si continúa utilizando el acceso *p y se ha eliminado la memoria, obtendrá un error de tiempo de ejecución.

Esto funcionará bien si la memoria a la que se apunta y Los punteros con los que estás apuntando son ambos POD.En este caso, no se llamaría a ningún destructor de todos modos y el asignador de memoria no conoce ni le importa el tipo almacenado en la memoria.

El único caso en el que esto está bien con tipos que no son POD es si la punta es un subtipo del puntero (p. ej.Estás apuntando a un Coche con un Vehículo*) y el destructor del puntero ha sido declarado virtual.

Esto no es seguro y ninguna de las respuestas hasta ahora ha enfatizado lo suficiente la locura de hacer esto.Simplemente no lo hagas si te consideras un verdadero programador o si alguna vez quieres trabajar como programador profesional en un equipo.Solo puedes decir que tu estructura contiene un no destructor. en este momento, sin embargo, está tendiendo una desagradable trampa posiblemente específica del compilador y del sistema para el futuro.Además, es poco probable que su código funcione como se esperaba.Lo mejor que puedes esperar es que no falle.Sin embargo, sospecho que poco a poco se producirá una pérdida de memoria, ya que las asignaciones de matrices mediante new muy a menudo asignan memoria adicional en los bytes. previo al puntero devuelto.No estarás liberando la memoria que crees que estás.Una buena rutina de asignación de memoria debería detectar esta discrepancia, al igual que herramientas como Lint, etc.

Simplemente no hagas eso y elimina de tu mente cualquier proceso de pensamiento que te haya llevado a considerar semejante tontería.

Cambié el código para usar malloc/free.Si bien sé cómo MSVC implementa la función nueva/eliminar para datos antiguos (y SOME_STRUCT en este caso era una estructura Win32, C tan simple), solo quería saber si era una técnica portátil.

No lo es, así que usaré algo que sí lo sea.

Si usa malloc/free en lugar de new/delete, a malloc y free no les importará el tipo.

Entonces, si está utilizando un POD tipo C (datos antiguos y simples, como un tipo integrado o una estructura), puede asignar algún tipo y liberar otro. tenga en cuenta que este es un mal estilo incluso si funciona.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top