Supongamos que tengo el siguiente código:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
¿Es esto seguro? ¿O se debe ptr
convertir char*
antes de eliminarlo?
Supongamos que tengo el siguiente código:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
¿Es esto seguro? ¿O se debe ptr
convertir char*
antes de eliminarlo?
Respuestas:
Depende de "seguro". Por lo general, funcionará porque la información se almacena junto con el puntero sobre la asignación en sí, por lo que el desasignador puede devolverla al lugar correcto. En este sentido, es "seguro" siempre que su asignador utilice etiquetas de límites internas. (Muchos hacen.)
Sin embargo, como se mencionó en otras respuestas, eliminar un puntero vacío no llamará a los destructores, lo que puede ser un problema. En ese sentido, no es "seguro".
No hay una buena razón para hacer lo que está haciendo de la forma en que lo está haciendo. Si desea escribir sus propias funciones de desasignación, puede usar plantillas de funciones para generar funciones con el tipo correcto. Una buena razón para hacerlo es generar asignadores de grupos, que pueden ser extremadamente eficientes para tipos específicos.
Como se mencionó en otras respuestas, este es un comportamiento indefinido en C ++. En general es bueno evitar comportamientos indefinidos, aunque el tema en sí es complejo y está lleno de opiniones encontradas.
fuente
sizeof(T*) == sizeof(U*)
para todosT,U
sugiere que debería ser posible tener 1void *
implementación de recolector de basura basada en plantillas . Pero luego, cuando el gc realmente tiene que eliminar / liberar un puntero, surge exactamente esta pregunta. Para que funcione, o necesita envoltorios destructores de función lambda (urgh) o necesitaría algún tipo de "tipo como datos" dinámico que permita ir y venir entre un tipo y algo almacenable.La eliminación mediante un puntero vacío no está definida por el estándar C ++; consulte la sección 5.3.5 / 3:
Y su nota al pie:
.
fuente
NULL
alguna diferencia en la gestión de la memoria de la aplicación?No es una buena idea y no es algo que haría en C ++. Estás perdiendo la información de tu tipo sin ningún motivo.
No se llamará a su destructor en los objetos en su matriz que está eliminando cuando lo llame para tipos no primitivos.
En su lugar, debe anular nuevo / eliminar.
Eliminar el vacío * probablemente liberará su memoria correctamente por casualidad, pero está mal porque los resultados no están definidos.
Si por alguna razón desconocida para mí necesita almacenar su puntero en un vacío * y luego libérelo, debe usar malloc y free.
fuente
Eliminar un puntero vacío es peligroso porque no se llamará a los destructores en el valor al que realmente apunta. Esto puede resultar en pérdidas de memoria / recursos en su aplicación.
fuente
La pregunta no tiene sentido. Su confusión puede deberse en parte al lenguaje descuidado que la gente usa con frecuencia
delete
:Usas
delete
para destruir un objeto que fue asignado dinámicamente. Hazlo, forma una expresión de eliminación con un puntero a ese objeto . Nunca "borras un puntero". Lo que realmente hace es "eliminar un objeto identificado por su dirección".Ahora vemos por qué la pregunta no tiene sentido: un puntero vacío no es la "dirección de un objeto". Es solo una dirección, sin semántica. Se puede haber venido de la dirección de un objeto real, pero se pierde esa información, ya que fue codificada en el tipo del puntero originales. La única forma de restaurar un puntero de objeto es devolver el puntero vacío a un puntero de objeto (lo que requiere que el autor sepa lo que significa el puntero).
void
en sí mismo es un tipo incompleto y, por lo tanto, nunca el tipo de un objeto, y un puntero vacío nunca puede usarse para identificar un objeto. (Los objetos se identifican conjuntamente por su tipo y su dirección).fuente
delete
puede ser un valor de puntero nulo, un puntero a un objeto que no es una matriz creado por una nueva expresión anterior o un puntero a un subobjeto que representa una clase base de dicho objeto. De lo contrario, el comportamiento no está definido ". Entonces, si un compilador acepta su código sin un diagnóstico, no es más que un error en el compilador ...delete void_pointer
. Es un comportamiento indefinido. Los programadores nunca deben invocar un comportamiento indefinido, incluso si la respuesta parece hacer lo que el programador quería que se hiciera.Si realmente tiene que hacer esto, ¿por qué no cortar al hombre medio (las
new
ydelete
los operadores) y llamar a lo globaloperator new
yoperator delete
directa? (Por supuesto, si está tratando de instrumentar los operadoresnew
ydelete
, en realidad debería volver a implementaroperator new
yoperator delete
).Tenga en cuenta que
malloc()
, a diferencia de ,operator new
arrojastd::bad_alloc
fallos (o llama alnew_handler
si hay uno registrado).fuente
Porque char no tiene una lógica destructora especial. ESTO no funcionará.
No llamarán al d'ctor.
fuente
Si desea usar void *, ¿por qué no usa solo malloc / free? new / delete es más que solo administrar la memoria. Básicamente, new / delete llama a un constructor / destructor y están sucediendo más cosas. Si solo usa tipos integrados (como char *) y los elimina a través de void *, funcionaría, pero aún así no se recomienda. La conclusión es usar malloc / free si desea usar void *. De lo contrario, puede utilizar funciones de plantilla para su conveniencia.
fuente
Mucha gente ya ha comentado diciendo que no, no es seguro eliminar un puntero vacío. Estoy de acuerdo con eso, pero también quería agregar que si está trabajando con punteros vacíos para asignar matrices contiguas o algo similar, puede hacer esto
new
para que pueda usarlo dedelete
manera segura (con, ejem , un poco de trabajo extra). Esto se hace asignando un puntero vacío a la región de memoria (llamado 'arena') y luego suministrando el puntero a la arena a new. Consulte esta sección en las preguntas frecuentes de C ++ . Este es un enfoque común para implementar grupos de memoria en C ++.fuente
Difícilmente hay una razón para hacer esto.
En primer lugar, si no conoce el tipo de datos, y todo lo que sabe es que lo es
void*
, entonces realmente debería tratar esos datos como un blob sin tipo de datos binarios (unsigned char*
) y usarmalloc
/free
para tratarlos. . Esto es necesario a veces para cosas como datos de forma de onda y similares, donde necesita pasarvoid*
punteros a C apis. Esta bien.Si lo hace saber el tipo de los datos (es decir, tiene una ctor / dtor), pero por alguna razón que terminó con un
void*
puntero (por cualquier razón que tenga) entonces usted realmente debería echarlo hacia atrás con el tipo que te des cuenta de sé , y llámalodelete
.fuente
He usado void *, (también conocido como tipos desconocidos) en mi marco durante la reflexión de código y otras hazañas de ambigüedad, y hasta ahora, no he tenido problemas (pérdida de memoria, violaciones de acceso, etc.) de ningún compilador. Solo advertencias debido a que el funcionamiento no es estándar.
Tiene mucho sentido eliminar un desconocido (vacío *). Solo asegúrese de que el puntero siga estas pautas, o puede dejar de tener sentido:
1) El puntero desconocido no debe apuntar a un tipo que tenga un deconstructor trivial, por lo que cuando se lanza como puntero desconocido NUNCA SE DEBE BORRAR. Solo elimine el puntero desconocido DESPUÉS de convertirlo de nuevo en el tipo ORIGINAL.
2) ¿Se hace referencia a la instancia como un puntero desconocido en la memoria enlazada a la pila o al montón? Si el puntero desconocido hace referencia a una instancia en la pila, ¡NUNCA SE DEBE BORRAR!
3) ¿Está 100% seguro de que el puntero desconocido es una región de memoria válida? No, ¡NUNCA SE DEBE BORRAR!
En total, hay muy poco trabajo directo que se pueda hacer usando un tipo de puntero desconocido (vacío *). Sin embargo, indirectamente, el vacío * es un gran activo en el que los desarrolladores de C ++ pueden confiar cuando se requiere ambigüedad de datos.
fuente
Si solo desea un búfer, use malloc / free. Si debe usar new / delete, considere una clase contenedora trivial:
fuente
Para el caso particular de char.
char es un tipo intrínseco que no tiene un destructor especial. Así que los argumentos de las filtraciones son discutibles.
sizeof (char) suele ser uno, por lo que tampoco hay un argumento de alineación. En el caso de una plataforma rara donde el tamaño de (char) no es uno, asignan memoria alineada lo suficiente para su char. Así que el argumento de la alineación también es discutible.
malloc / free sería más rápido en este caso. Pero pierde std :: bad_alloc y tiene que verificar el resultado de malloc. Llamar a los operadores globales nuevos y de eliminación podría ser mejor, ya que evita al intermediario.
fuente
new
realidad está definido lanzar. Esto no es verdad. Depende del compilador y del modificador del compilador. Consulte, por ejemplo,/GX[-] enable C++ EH (same as /EHsc)
conmutadores MSVC2019 . También en los sistemas integrados, muchos eligen no pagar los impuestos sobre el rendimiento de las excepciones de C ++. Entonces, la oración que comienza con "Pero pierdes std :: bad_alloc ..." es cuestionable.