Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.9.21  Los operadores delete y delete[ ]

§1  Presentación

Los operadores delete y delete[] sirven respectivamente para destruir los objetos creados con new y new[], volviendo a dejar la memoria asignada a disposición del gestor de memoria del compilador.

Los objetos creados con new deben ser destruidos necesariamente con delete, y que las matrices creadas con new[ ] deben ser borradas con delete[ ].

§2  El operador delete

El operador delete (palabra clave) ofrece una desasignación dinámica de memoria (del montón 1.3.2), desasignando un bloque previamente asignado con new ( 4.9.20). Es similar pero superior a la función free de la Librería Estándar.

Este operador debe utilizarse para remover toda la memoria previamente asignada con new; de no hacerse así, puede producirse un agotamiento de la memoria. Sin embargo no puede usarse para desasignar memoria previamente asignada con malloc [2].

Es muy frecuente encontrar a este operador en destructores de clases ( 4.11.2d2), a fin de desasignar el espacio de los objetos cuando dicho espacio fue asignado con el operador new en el constructor ( 4.11.2d1).

§3  Sintaxis

<::> delete <cast-expression>
<::> delete [ ] <cast-expression>  // para matrices
delete <array-name> [ ];           // para matrices

§4  Ejemplo

char* p;
void func() {
   p = new char[10];     // asigna espacio para 10 char
   ...
   delete[] p;           // desasigna el espacio anterior [1]
}

§4.1 Ejemplo

void* sptr;            // puntero genérico
(std::string*) sptr = new std::string[10];    // L.2
...
delete [] (std::string*) sptr;                // L.4

std::string es un calificador de tipo; se refiere a un objeto tipo string [3]. Observe que L.2 realiza la asignación de espacio para el objeto (en este caso el contenedor está previsto para 10 caracteres) y además realiza un modelado ( 4.9.9) sobre el puntero genérico sptr para adecuarlo al tipo devuelto por new. Finalmente, la sentencia L4 restituye el espacio reservado en L2.

Ver tambien ejemplos en: 4.9.20c4.3.3; 4.3.7 ( ejemplo)

§5  Precauciones con delete

Cuando este operador se utiliza con punteros (como en el ejemplo), es un gran error volver a utilizar la misma sentencia con el mismo puntero (esto puede suceder por error). Considere el siguiente código:

delete ptr;
...
delete ptr;       // Error!!


Si las sentencias entre ambos delete no modifican el valor del puntero ptr, puede producirse un error grave. Recuerde que la desasignación de la memoria señalada por ptr no implica alteración del Rvalue(ptr), así que lo mejor es hacer:

delete ptr;
ptr = 0;
...
delete ptr;       // Ahora sin riesgo!

ya que por definición, el operador delete sobre un puntero nulo ( 4.2.1) es una operación nula. Por esta razón, si se sobrecarga este operador, puede ser buena idea incluir en la función asignar cero al puntero:

tipoX::operator delete(ptr) {

   ...

   ptr = NULL;

}

Idéntica situación de riesgo se presenta cuando más de un puntero señalan al mismo objeto:

C* cptr = new C;

C* dptr = cptr;

...

delete cptr;

delete dptr;        // Error!!


También cuando el puntero inicial ha sido modificado inadvertidamente:

char* p = new char[10];

...

p++;

...

delete[] p;         // Error!!


Recuerde que es un error aplicar el operador delete a un puntero cuyo valor no haya sido previamente asignado por new. Por ejemplo:

void (* fptr)();

void func() {...}

const int ki;

const int* kptr;

...

ftpr = func;

delete fptr;        // Error!! intento de borrar puntero a función

kptr = &ki;

delete kptr;        // Error!! intento de borrar puntero a constante

§6  Sobrecarga de los operadores delete/delete[ ]

No es posible sobrecargar la versión global de las funciones-operador ::operator delete() y ::operator delete()[ ]. Sin embargo es posible ocultarlas con versiones globales propias, aunque solo es posible definir una versión global de cada operador en cada aplicación.

Estas versiones deben devolver void, y su primer argumento debe ser un puntero-a-void (void*). Se admite un segundo argumento opcional de tipo size_t para indicar la cantidad de espacio a liberar. Por consiguiente, el prototipo puede ser algo así:

void delete(void* Type_ptr [, size_t Type_size]) { ... };
void delete[](void* Type_ptr [, size_t Type_size]) { ... };


Las clases C que tengan su propia versión de los operadores new() y/o new()[], pueden tener también sus propias versiones sobrecargadas de los operadores delete y delete[]. Para ello se definen versiones propias de las funciones-operador C::operator delete[]() y C::operator delete() según los siguientes prototipos:

void operator delete(void* Type_ptr [, size_t Type_size]);
void operator delete[](size_t Type_ptr [, size_t Type_size]);


  Type_ptr es un puntero-al-objeto (instancia de clase) que se desea destruir.

  Type_size es un argumento opcional tipo size_t indicando el tamaño del objeto.

§7  El operador delete con matrices

Para borrar matrices debe utilizarse la segunda forma de la sintaxis:

delete [] <array-name>;

Ejemplo

for (i = 0; i < fil; i++)   // Paso 1: Borrar columnas
  delete [] x[i];
delete [] x;                // paso 2: borrar filas


Observe que en estas expresiones el primer par de llaves [ ] están vacías, es la forma de indicar al operador delete que se trata del espacio de una matriz. Como puede verse en la segunda línea, opcionalmente puede indicarse un elemento a borrar.

En el capítulo dedicado a la Inicialización indirecta de matrices ( 4.3.7) puede encontrarse una completa descripción y ejemplos del proceso para crear y destruir matrices en el montón, lo que supone la utilización de los operadores new y delete.

  Inicio.


[1]  La versión 2.0 de Borland C++ requiere que se especifique el tamaño de la matriz en el comando delete. Por su parte la versión 5 ignora el tamaño si es especificado. En realidad, las primeras implementaciones exigían que el operador delete[ ] indicara el número de elementos a borrar, lo que en ocasiones era origen de errores, por lo que se decidió delegar esta responsabilidad en el compilador.

[2]  En otras palabras: no pueden mezclarse los sistemas C y C++ de asignación de memoria, y aunque puedan coexistir en el mismo programa se recomienda evitarlo.

[3]  string es un contenedor definido en la Librería Estándar C++ para almacenar cadenas de caracteres. Su ventaja es que proporciona utilidades para manejo de estos tipos que son equiparables a las que presentan otros lenguajes de alto nivel para manejar estas cadenas. Comparación, concatenación, tratamiento de subcadenas, Etc.