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]


El operador new:  ejemplo resumen

El siguiente ejemplo ilustra la mayoría de conceptos relativos a los operadores new y delete, en las versiones general y para matrices. Se trata de un ejemplo didáctico adecuado para mostrar la operatoria. Por simplicidad no se ha incluido ningún sistema de protección o control de excepciones para comprobar que efectivamente no se producen errores durante el proceso.

#include <iostream>
using namespace std; 

class Alpha {            // Un tipo abstracto
  static int tot;
  public:
  char buf[6];
  Alpha() {              // Constructor-1 (por defecto)
    tot++;
    cout << "Constructor-1: " << tot << " objetos" << endl;
    if (tot < 4) strcpy(buf, "AEIOU");
    else         strcpy(buf, "12345");
  }
  Alpha(char* s) {       // constructor-2
    tot++;
    cout << "Constructor-2: " << tot << " objetos" << endl;
    strcpy(buf, s);
  }
  ~Alpha() {             // destructor
    tot--;
    cout << "Destructor Alpha: " << tot << " objetos" << endl;
  }
  void* operator new(size_t size) {   // operator new
    cout << "Version new de Alpha" << endl;
    return ::operator new(size));
  }
  void operator delete(void* ptr) {   // operator delete
    cout << "Version delete de Alpha" << endl;
  }
};
int Alpha::tot = 0;

void main() {                   // ===================
  Alpha* pt1 = new Alpha;       // M1:
  cout << "  objeto1.buf = " << pt1->buf << endl;

  Alpha* pt2 = new Alpha ("aeiou");  // M4:
  cout << "  objeto2.buf = " << pt2->buf << endl;

  Alpha* pt3 = new Alpha [3];   // M7:
  cout << "  objeto3[0].buf = " << pt3->buf << endl;
  cout << "  objeto3[1].buf = " << (pt3+1)->buf << endl;

  delete[] pt3;                 // M11:
  delete pt2;                   // M12:
  pt1->Alpha::~Alpha();         // M13:
  cout << "Fin" << endl;
}

Salida:

Version new de Alpha
Constructor-1: 1 objetos
  objeto1.buf = AEIOU
Version new de Alpha
Constructor-2: 2 objetos
  objeto2.buf = aeiou
Constructor-1: 3 objetos
Constructor-1: 4 objetos
Constructor-1: 5 objetos
  objeto3[0].buf = AEIOU
  objeto3[1].buf = 12345
Destructor Alpha: 4 objetos
Destructor Alpha: 3 objetos
Destructor Alpha: 2 objetos
Destructor Alpha: 1 objetos
Version delete de Alpha
Destructor Alpha: 0 objetos
Fin

Comentario

El programa define un tipo abstracto ( 2.2), la clase Alpha, que solo tiene dos propiedades;  una matriz de caracteres buf y un entero tot. Este último es estático ( 4.11.7), y nos servirá para llevar la cuenta de los objetos creados de la clase.

Además de lo anterior se definen dos constructores, un destructor y sendas versiones sobrecargadas de las funciones-operador new() y delete() para los objetos de la clase. Tanto los constructores como el destructor incrementan y decrementan el contador de objetos y muestran un mensaje con la cantidad resultante.

El constructor-2 acepta como argumento una cadena de caracteres [1] y utiliza la función de librería strcpy para iniciar el miembro buf con la cadena recibida.  Por su parte el constructor por defecto inicia la matriz buf con un valor constante. La única particularidad es que este valor es "AEIOU" si se han creado menos de 4 objetos, y "12345" para el objeto 4º y siguientes.

Observe que el método Alpha::operator new() utiliza a su vez una invocación a la versión global para recabar al gestor de memoria el espacio correspondiente.

La función main se limita a crear tres objetos en el montón y a destruirlos después.  A continuación lanza un mensaje antes de terminar.

La sentencia M1 es responsable de las dos primeras salidas. El Rvalue new Alpha es convertido por el compilador en una invocación a la versión sobrecargada de new() seguida de una invocación al constructor de la clase para iniciar un objeto en la zona asignada:

Alpha::new(sizeof(Alpha));
Alpha::Alpha();

Observe que en este caso se ha utilizado el constructor por defecto porque el operador new no ha utilizado con ningún iniciador.

La sentencia M2 responsable de la tercera salida, es un simple recordatorio de que las cosas han ocurrido como suponíamos.

En M4 se crea un nuevo objeto y M5 comprueba el valor obtenido. Aquí la sucesión de hechos es prácticamente la misma que en las sentencias anteriores. M4 es responsable de las salidas 4 y 5. La diferencia con M1 es que aquí se ha utilizado un iniciador, de forma que el compilador la transforma en:

Alpha::new(sizeof(Alpha));
Alpha::Alpha("aeiou");

En este caso se utiliza el constructor-2, tal como lo evidencia las salidas 5 y 6.

En M7 se crea  una matriz de tres tipos Alpha utilizando la versión new[ ] para matrices, la llamaremos mAlpha [2]. Es interesante observar que como la clase no dispone de su propia versión de la función operator new[](), se invoca la versión global para recabar el espacio correspondiente. A continuación se invoca el constructor por defecto para cada miembro de la matriz, lo que produce las salidas 7, 8 y 9.

Recordemos que el miembro buf del objeto mAlpha[0] se inicia con la cadena "AEIOU", pero los otros dos miembros de la matriz se inician con la cadena "12345". Esto es precisamente lo que se comprueba en las salidas 10 y 11 producidas por las sentencias M8 y M9. Observe especialmente la forma en que se ha obtenido el objeto mAlpha[1] en M9).


La destrucción de objetos se realiza en orden inverso al de creación:

El objeto mAlpha creado en M7 con new[], es destruido en M11 utilizando delete[]. Al llegar a este punto, el compilador invoca al destructor de la clase:

Alpha::~Alpha();

La invocación se realiza tres veces, una para cada miembro de mAlpha, lo que produce las salidas 12, 13 y 14. A continuación, y dado que Alpha no dispone de su propia versión de operator delete[](), se invoca la versión global para restituir al gestor de memoria el espacio reservado para esta matriz.

En M12 se destruye con delete el objeto creado en M4. Aquí también es invocado automáticamente el destructor Alpha::~Alpha() para destruir el objeto (lo que produce la salida 15). A continuación, el compilador utiliza el método Alpha::operator delete() para rehusar el espacio ocupado. Este invocación es responsable de la salida 16.

Finalmente, la destrucción del primer objeto se realiza en M13 mediante una invocación directa a su destructor. Es la salida 17, después de lo cual el programa termina con un mensaje de aviso.

Es importante observar que, a pesar de que el contador de objetos avisa que se han destruido todos, el programa contiene un grave error que le haría perder memoria si el contenido de main estuviera en una función de un programa mayor. Intente encontrar por sí mismo la respuesta y las medidas correctoras adecuadas. En caso de duda pulse aquí ( Respuesta)

  Inicio.


[1]  En realidad un puntero-a-carácter.  Ver ( 3.2.3f).

[2]  La matriz en sí es una entidad anónima (carece de nombre), es accesible a través de su puntero que sí tiene nombre (pt3), pero que es un objeto distinto de la matriz.