c++ - ultimo - listas enlazadas eliminar nodo
¿Es seguro eliminar un puntero vacío? (13)
Supongamos que tengo el siguiente código:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
¿Esto es seguro? ¿O debe ptr
ser lanzado a char*
antes de la eliminación?
Apenas hay una razón para hacer esto.
En primer lugar, si no sabes el tipo de datos, y todo lo que sabes es que es void*
, entonces realmente deberías tratar esos datos como una masa unsigned char*
letra de datos binarios ( unsigned char*
), y usar malloc
/ free
para manejarlo. Esto se requiere a veces para cosas como datos de forma de onda y similares, donde debe pasar punteros void*
a C apis. Esta bien.
Si conoce el tipo de datos (es decir, tiene un ctor / dtor), pero por alguna razón terminó con un puntero void*
(por la razón que tenga) entonces debería devolverlo al tipo que conoce ser y llamar a delete
en él.
Depende de "seguro". Por lo general, funcionará porque la información se almacena junto con el puntero sobre la asignación en sí misma, por lo que el desasignador puede devolverla al lugar correcto. En este sentido, es "seguro" siempre que su asignador utilice etiquetas de límite internas. (Muchos hacen.)
Sin embargo, como se mencionó anteriormente, al eliminar un puntero vacío no se llamarán 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 manera 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 grupo, que pueden ser extremadamente eficientes para tipos específicos.
Eliminar un puntero volador es peligroso porque no se invocarán destructores sobre el valor al que realmente apunta. Esto puede provocar fugas de memoria / recursos en su aplicación.
He utilizado void *, (también conocido como tipos desconocidos) en mi framework para reflexionar sobre el código y otras proezas de ambigüedad, y hasta ahora, no he tenido problemas (pérdida de memoria, violaciones de acceso, etc.) de ningún compilador. Solo las advertencias debido a que la operación no es estándar.
Tiene sentido borrar un desconocido (void *). Solo asegúrate 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 lo arroje como un puntero desconocido, NUNCA DEBE ELIMINARSE. Solo elimine el puntero desconocido DESPUÉS de devolverlo al tipo ORIGINAL.
2) ¿Se hace referencia a la instancia como un puntero desconocido en la memoria enlazada a la pila o encuadernada en el montón? Si el puntero desconocido hace referencia a una instancia en la pila, ¡NUNCA DEBE BORRARSE!
3) ¿Es 100% positivo que el puntero desconocido sea una región de memoria válida? No, entonces NUNCA DEBERÍA SER DELTADO!
En total, hay muy poco trabajo directo que se puede hacer usando un tipo de puntero desconocido (void *). Sin embargo, indirectamente, el vacío * es un gran activo para los desarrolladores de C ++ para confiar cuando se requiere ambigüedad de datos.
La eliminación a través de un puntero de vacío no está definida por el estándar de C ++; consulte la sección 5.3.5 / 3:
En la primera alternativa (eliminar objeto), si el tipo estático del operando es diferente de su tipo dinámico, el tipo estático debe ser una clase base del tipo dinámico del operando y el tipo estático debe tener un destructor virtual o el comportamiento no está definido . En la segunda alternativa (eliminar matriz) si el tipo dinámico del objeto a eliminar difiere de su tipo estático, el comportamiento no está definido.
Y su nota al pie:
Esto implica que un objeto no se puede eliminar utilizando un puntero de tipo void * porque no hay objetos de tipo void
.
La pregunta no tiene sentido. Su confusión puede deberse en parte al lenguaje descuidado que la gente suele usar con delete
:
Utiliza delete
para destruir un objeto que se asignó 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 que se identifica por su dirección".
Ahora vemos por qué la pregunta no tiene sentido: un puntero de vacío no es la "dirección de un objeto". Es solo una dirección, sin ninguna semántica. Puede haber provenido 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 forma de restablecer un puntero de objeto es convertir el puntero de vacío en un puntero de objeto (lo que requiere que el autor sepa qué significa el puntero). void
sí mismo es un tipo incompleto y, por lo tanto, nunca el tipo de un objeto, y un puntero vacío nunca se puede utilizar para identificar un objeto. (Los objetos se identifican conjuntamente por su tipo y su dirección).
Mucha gente ya comentó 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 con new
para que pueda usar delete
forma segura (con , ejem, un poco de trabajo extra). Esto se hace asignando un puntero vacío a la región de memoria (llamada ''arena'') y luego suministrando el puntero a la arena a nuevo. Vea esta sección en las preguntas frecuentes de C ++ . Este es un enfoque común para implementar pools de memoria en C ++.
No es una buena idea ni algo que harías en C ++. Está perdiendo su tipo de información sin ningún motivo.
No se invocará su destructor sobre los objetos en su matriz que está eliminando cuando lo llama para tipos no primitivos.
En su lugar, debe anular el nuevo / eliminar.
Eliminar el vacío * probablemente liberará la 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 liberarlo, debe usar malloc y gratis.
Para el caso particular de char.
char es un tipo intrínseco que no tiene un destructor especial. Entonces los argumentos de las filtraciones son discutibles.
sizeof (char) suele ser uno, por lo que tampoco hay argumento de alineación. En el caso de una plataforma rara donde el tamaño de (char) no es uno, asignan memoria lo suficientemente alineada para su char. Entonces el argumento de alineación también es discutible.
malloc / free sería más rápido en este caso. Pero pierdes std :: bad_alloc y tienes que verificar el resultado de malloc. Llamar a los operadores globales nuevos y eliminar podría ser mejor ya que omite al intermediario.
Porque char no tiene una lógica de destrucción especial. ESTO no funcionará
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
El d''ctor no será llamado.
Si desea usar void *, ¿por qué no usa solo malloc / free? new / delete es más que solo administración de memoria. Básicamente, new / delete llama a un constructor / destructor y hay más cosas que están pasando. Si solo usa tipos incorporados (como char *) y los elimina a través de void *, funcionaría, pero aún así no es recomendable. La conclusión es usar malloc / free si quieres usar void *. De lo contrario, puede usar 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);
}
Si realmente debe hacer esto, ¿por qué no cortar el intermediario (los operadores new
y delete
) y llamar al operator new
global operator new
y operator delete
directamente? (Por supuesto, si intentas instrumentar a los operadores new
y delete
, en realidad deberías volver a implementar el 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()
, el operator new
arroja std::bad_alloc
en el fallo (o llama al new_handler
si hay uno registrado).
Si solo quieres un buffer, usa malloc / free. Si debe usar new / delete, considere una clase de contenedor 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;