¿Es seguro eliminar un puntero void?


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 debe ptr ser lanzado a char* antes de la eliminación?

Author: An̲̳̳drew, 2009-06-03

13 answers

Depende de "safe."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 y cuando su asignador use etiquetas de límite interno. (Muchos lo hacen.)

Sin embargo, como se mencionó anteriormente, eliminar un puntero void no llamará a destructores, lo que puede ser un problema. En ese sentido, no es "seguro."

No hay una buena razón para hacer lo que estás haciendo de la manera lo estás 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 comportamiento indefinido en C++. En general, es bueno evitar el comportamiento indefinido, aunque el tema en sí es complejo y está lleno de opiniones contradictorias.

 14
Author: Christopher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-08 19:43:36

Eliminar mediante un puntero void no está definido por el estándar de C++ - ver sección 5.3.5 / 3:

En la primera alternativa (eliminar objeto), si el tipo estático de la operando es diferente de su dinámica tipo, el tipo estático será una base clase del tipo dinámico del operando y el tipo estático tendrá un virtual destructor o el comportamiento es indefinido. En la segunda alternativa (delete array) si el tipo dinámico de el objeto a borrar difiere de su tipo estático, el comportamiento es indefinido.

Y su nota al pie:

Esto implica que un objeto no puede ser eliminado usando un puntero de tipo void* porque no hay objetos de tipo void

.

 130
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-03 14:56:03

No es una buena idea y no es algo que harías en C++. Usted está perdiendo su información de tipo sin ninguna razón.

Su destructor no será llamado en los objetos de su matriz que está borrando cuando lo llama para tipos no primitivos.

En su lugar, debe anular nuevo / eliminar.

Eliminar el void* 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í necesitas para almacenar su puntero en un vacío * y luego liberarlo, debe usar malloc y free.

 23
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-02 21:17:41

Eliminar un puntero void es peligroso porque no se llamará a los destructores sobre el valor al que realmente apunta. Esto puede dar lugar a fugas de memoria / recursos en su aplicación.

 13
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-02 21:05:14

La pregunta no tiene sentido. Su confusión puede deberse en parte al lenguaje descuidado que la gente suele usar con delete:

Se usa deletepara destruir un objeto que fue asignado dinámicamente. Si lo hace, formará una expresión delete con un puntero a ese objeto. Nunca "borras un puntero". Lo que realmente haces es "eliminar un objeto que se identifica por su dirección".

Ahora vemos por qué la pregunta no tiene sentido: Un puntero vacío no es la " dirección de objeto". Es sólo una dirección, sin semántica. puede queprovenga de la dirección de un objeto real, pero esa información se pierde, porque estaba codificada en el tipo del puntero original. La única manera 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 se puede usar para identificar un objeto. (Los objetos se identifican conjuntamente por su tipo y su dirección.)

 6
Author: Kerrek SB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-08 12:35:14

Porque char no tiene una lógica destructora especial. ESTO no funcionará.

class foo
{
   ~foo() { printf("huzza"); }
}

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}

El d'ctor no será llamado.

 5
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-02 21:07:57

Si quieres usar void*, ¿por qué no usas solo malloc/free? new / delete es algo más que administrar la memoria. Básicamente, new / delete llama a un constructor / destructor y hay más cosas sucediendo. 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 quieres usar void*. De lo contrario, puede utilizar funciones de plantilla para su conveniencia.

template<typename T>
T* my_alloc (size_t size)
{
   return new T [size];
}

template<typename T>
void my_free (T* ptr)
{
   delete [] ptr;
}

int main(void)
{
    char* pChar = my_alloc<char>(10);
    my_free(pChar);
}
 5
Author: young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-02 21:33:07

Si realmente debe hacer esto, ¿por qué no cortar el intermediario (los operadores new y delete) y llamar al global operator new y operator delete directamente? (Por supuesto, si está tratando de instrumentar los operadores new y delete, en realidad debe reimplementar operator new y operator delete.)

void* my_alloc (size_t size)
{
   return ::operator new(size);
}

void my_free (void* ptr)
{
   ::operator delete(ptr);
}

Tenga en cuenta que a diferencia de malloc(), operator new lanza std::bad_alloc en caso de fallo (o llama al new_handler si está registrado).

 5
Author: bk1e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-04 07:34:10

Muchas personas ya han comentado diciendo que no, no es seguro eliminar un puntero vacío. Estoy de acuerdo con eso, pero también quería añadir que si estás trabajando con punteros void para asignar arrays contiguos o algo similar, puedes hacer esto con new para que puedas usar delete de forma segura (con, ejem, un poco de trabajo extra). Esto se hace asignando un puntero void a la región de memoria (llamado 'arena') y luego suministrando el puntero a la arena a new. Consulte esta sección en C++ FAQ . Este es un enfoque común para implementar pools de memoria en C++.

 5
Author: Paul Morie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-25 20:58:54

Difícilmente hay una razón para hacer esto.

En primer lugar, si no conoce el tipo de los datos, y todo lo que sabe es que es void*, entonces realmente debería tratar esos datos como un blob sin tipo de datos binarios (unsigned char*), y usar malloc/free para lidiar con ello. Esto se requiere a veces para cosas como datos de forma de onda y similares, donde necesita pasar void* punteros a las api de C. Eso está bien.

Si haces conocer el tipo de data (es decir, tiene un ctor/dtor), pero por alguna razón terminaste con un puntero void* (por cualquier razón que tengas) entonces realmente deberías enviarlo de nuevo al tipo que sabes que es, y llamar a delete en él.

 0
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-07 01:25:55

He usado void*, (aka unknown types) en mi framework durante while in code reflection y otras hazañas de ambigüedad, y hasta ahora, no he tenido problemas (fuga de memoria, violaciones de acceso, etc.) de cualquier compilador. Solo advertencias debido a que la operación no es estándar.

Tiene sentido eliminar un desconocido (void*). 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 deconstructor, y por lo tanto, cuando se castea como un puntero desconocido, NUNCA SE DEBE ELIMINAR. Solo elimine el puntero desconocido DESPUÉS de fundirlo de nuevo en el tipo ORIGINAL.

2) ¿Se hace referencia a la instancia como un puntero desconocido en la memoria enlazada a pila o a montón? Si el puntero desconocido hace referencia a una instancia en la pila, ¡nunca DEBE ELIMINARSE!

3) ¿Está 100% seguro de que el puntero desconocido es una región de memoria válida? No, entonces NUNCA debe SER DELTED!

En todo, hay muy poco trabajo directo que se puede hacer usando un tipo de puntero desconocido (vacío*). Sin embargo, indirectamente, el void * es un gran activo en el que los desarrolladores de C++ pueden confiar cuando se requiere ambigüedad de datos.

 0
Author: zackery.fix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-04 23:17:05

Si solo quieres un buffer, usa malloc/free. Si debe usar new/delete, considere una clase de envoltura trivial:

template<int size_ > struct size_buffer { 
  char data_[ size_]; 
  operator void*() { return (void*)&data_; }
};

typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer

OpaqueBuffer* ptr = new OpaqueBuffer();

delete ptr;
 0
Author: Sanjaya R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-13 14:59:43

Para el caso particular de char.

Char es un tipo intrínseco que no tiene un destructor especial. Así que los argumentos de fugas es discutible.

Sizeof(char) suele ser uno, por lo que tampoco hay ningún argumento de alineación. En el caso de una plataforma rara donde el sizeof (char) no es uno, asignan memoria lo suficientemente alineada para su char. Así que el argumento de alineación también es discutible.

Malloc/free sería más rápido en este caso. Pero usted pierde std:: bad_alloc y tiene que compruebe el resultado de malloc. Llamar a los operadores globales nuevo y eliminar podría ser mejor, ya que omite el intermediario.

 0
Author: rxantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-01 03:47:02