domingo, 22 de junio de 2014

Página 12. Estructura de datos en C++

  • 2.4. AUTOREFERENCIA DEL OBJETO: THIS

this es un puntero al objeto que envía un mensaje, o simplemente, un puntero al objeto que llama a una función miembro de la clase (ésta no debe de ser static). Este puntero no se define, internamente se define:

const NombreClase* this;

Por consiguiente, no puede modificarse. Las variables y funciones de la clase están referenciados, implícitamente por this. Por ejemplo, la siguiente clase :


class Triangulo
{
private:
     double base, altura;
public:
     double area() const
     {
          return base * altura /2.0;
     }
};

En la función area() se hace referencia a las variables instancia base y altura. ¿A la base, altura de qué objeto? El método es común para todos los objetos Triangulo. Aparentemente no distingue entre un objeto y otro, sin embargo, cada variable instancia implícitamente está cualificada por this, es como si se hubiera escrito:


public double area()
{
     return this -> base * this -> altura/2.0;
}

Fundamentalmente this tiene dos usos:


  • Seleccionar explícitamente un miembro de una clase con el fin de dar más claridad o de evitar colisión de identificadores. Por ejemplo, en la clase Triangulo:

void datosTriangulo(double base, double altura)
{
     this -> base = base;
     this -> altura = altura;
}

Se ha evitado, con this, la colisión entre argumentos y variables instancia.


  • Que una función miembro devuelva el mismo objeto que le llamó. De esa manera se puede hacer llamadas es cascada a funciones de la misma clase. De nuevo en la clase Triangulo:
const Triangulo& datosTriangulo(double base, double altura)
{
     this -> base = base;
     this -> altura = altura;
     return *this;
}

const Triangulo& visualizar() const
{
     cout << " Base = " << base << endl;
     cout << " Altura = " << altura << endl;
     return *this;
}

Ahora se puede realizar esta concatenación de llamadas:


Triangulo t;
t.datosTriangulo(15.0, 12.0).visualizar();



  • 2.5. CLASES COMPUESTAS

Una clase compuesta es aquella que contiene miembros dato que son asimismo objeto de clases. Antes de crear el cuerpo de un constructor de una clase compuesta, se deben de construir los miembros datos individuales en su orden de declaración. La clase Estudiante contiene miembros dato de tipo Expediente y Dirección:

clase Expediente
{
public:
     Expediente();          // constructor por defecto
     Expedienre(int idt);

     // ...
};

class Direccion
{
public:
     Direccion();          // constructor por defecto
     Direccion(string d);
     // ...
};

class Estudiante
{
public:
     Estudiante()
     {
          PonerId(0);
          PonerNotaMedia(0.0);
     }
     void PonerId(long);
     void PonerNotaMedia(float);
private;
     long id;
     Expediente exp;
     Direccion dir;
     float NotMedia;
};


Aunque Estudiante contiene Expediente y Direccion, el constructor de Estudiante no tiene acceso a los miembros privados o protegidos de Expediente o Direccion.

Cuando un objeto Estudiante sale fuera de alcance, se llama a su destructor. El cuerpo de ~Estudiante() se ejecuta antes que los destructores de Expediente y Direccion. En otras palabras, el orden de llamadas a destructores a clases compuestas es exactamente el opuesto al orden de llamadas a constructores.

La llamada al constructor con argumentos de los miembros de una clase compuesta se hace desde el constructor de la clase compuesta. Por ejemplo, este constructor de Estudiante inicializa su expediente y dirección: 

Estudiante::Estudiante(int expediente, string direccion)
     :exp(expediente), dir(direccion)     // lista de inicialización
{
     PonerId(0);
     PonerNotaMedia(0.0);
}

Regla:
El orden de creación de un objeto compuesto es en, primer lugar, los objetos miembros en orden de aparición; a continuación el cuerpo del constructor de la clase compuesta.
La llamada al constructor de los objetos miembros se realiza en la lista de inicialización del constructor de la clase compuesta, con la sintaxis siguiente:

Compuesta(arg1, arg2, arg3,...): miembro1(arg1,...), miembro2(arg2,...)
{
     // cuerpo del constructor de clase compuesta
}


  • 2.6.1. Variables static

Las variables de la clase static son compartidas por todos los objetos de la clase. Se declaran de igual manera que otra variable, añadiendo, como prefijo, la palabra reservada static. Por ejemplo:

class Conjunto
{
     static int k;
     static Lista lista;
     // ...
}

Los miembros static de una clase deben ser analizados explícitamente fuera del cuerpo de la clase. Así los miembros, k y lista:

int Conjunto::k = 0;
Lista Conjunto::lista = NULL;

Dentro de las clases se accede a los miembros static de la manera habitual, simplemente con su nombre. Desde fuera de la clase se accede con el nombre de la clase, el selector, y el nombre de la variable, por ejemplo:

cout << " valor de k = " << Conjunto.k;

Formato:
El símbolo :: (operador de resolución de ámbitos) se utiliza en sentencias de ejecución que accede a los miembros estáticos de la clase. Por ejemplo, la expresión Punto::X se refiere al miembro dato estático X de la clase Punto.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.2.

Dada una clase se requiere conocer en todo el momento los objetos activos de la aplicación.

Se declara la clase Ejemplo con dos constructores y el constructor de copia. Todos incrementan la variable static cuenta, en 1. De esa manera cada nuevo objeto queda contabilizado. También se declara el destructor para decrementar cuenta en 1. main() crea objetos y visualiza la variable que contabiliza el número de sus objetos.


// Ejemplo.h
class Ejemplo
{
private:
     int datos;
public:
     static int cuenta;
     Ejemplo();
     Ejemplo(int g);
     Ejemplo(const Ejemplo&);
     ~Ejemplo();
};

// definición de la clase, archivo Ejemplo.cpp

#include "Ejemplo.h"

int Ejemplo::cuenta = 0;

Ejemplo::Ejemplo()
{
     datos = 0;
     cuenta++;           // nuevo objeto
}

Ejemplo::Ejemplo()
{
     datos = g;
     cuenta++;           // nuevo objeto
}

Ejemplo::Ejemplo(const Ejemplo& org)
{
     datos = org.datos;
     cuenta++;           // nuevo objeto
}

Ejemplo::~Ejemplo()
{
     cuenta--;
}

// programa de prueba, archivo Demostatic.cpp

#include 
using namespace std;
#include "Ejemplo.h";

int main()
{
     Ejemplo d1, d2;

     cout << "Objetos ejemplo: " << Ejemplo::cuenta << endl;
     if (true)
     {
          Ejemplo d3(88);
          cout << "Objetos ejemplo: " << Ejemplo::cuenta << endl;
     }
     cout << "Objetos ejemplo: " << Ejemplo::cuenta << endl;
     Ejemplo* pe;
     pe = new Ejemplo();
     cout << "Objetos ejemplo: " << Ejemplo::cuenta << endl;
     delete pe;
     cout << "Objetos ejemplo: " << Ejemplo::cuenta << endl;
     return 0; 
}


-------------------------------------------------------------------------------------------------------------------------------------------------------------------


  • 2.6.2. Funciones miembro static

Los métodos o miembros de una clase se llaman a través de los objetos. En ocasiones, interesa definir funciones que estén controlados por la clase, incluso que no haga falta crear un objeto para llamarlos, son las funciones miembro static. La llamada a estas funciones de clase se realiza a través de la clase: NombreClase::metodo(), respetando las reglas de visibilidad. También pueden llamar con un objeto de la clase, no es recomendable debido a que son métodos dependientes de la clase y no de los objetos.

Los métodos definidos como static no tienen asignado la referencia this por eso sólo pueden acceder a miembros static de la clase. Es un error que una función miembro static acceda a miembros de la clase no static.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.10. La clase SumaSerie define tres variables static, y un método static que calcula la suma cada vez que se llama.


class SumaSerie
{
private:
     static long n;
     static long m;
public:
     static long suma()
     {
           m += n;
           n = m - n;
           return m;
     }
};
long SumaSerie::n = 0;
long SumaSerie::m = 1;

-------------------------------------------------------------------------------------------------------------------------------------------------------------------


  • 2.7. FUNCIONES AMIGAS (FRIEND)

Con el mecanismo amigo (friend) se permite que funciones no miembro de una clase puedan acceder a sus miembros privados o protegidos. Se puede hacer friend de una clase una función global, o bien otra clase. En el siguiente ejemplo la función global distancia() se declara friend de la clase Punto.


double distancia (const Punto& P2)
{
     double d;
     d = sqrt((double)(P2.x * P2,x + P2.y * P2.y));
}
class Punto
{
     friend double distancia(const Punto& P2);
     //...
};

Si distancia() no fuera amiga de Punto no podrá acceder directamente a los miembros privado x e y. Es muy habitual sobrecargar el operador << para mostrar por pantalla los objetos de una clase con cout. Esto quiere decir que al igual que se escribe un número entero, por ejemplo:

ink k = 9; cout << " valor de k = " << k;

se pueda escribir un objeto Punto:

Punto p[1, 5]; cout << " Punto " << p;

Para conseguir esto hay que difinir la función global operator << y hacerla amiga de la clase, en el ejemplo de la clase Punto

ostream& operator << (ostream& pantalla, const Punto& mp)
{
     pantalla << " x = " << mp.x << " , y = " << mp.y << endl;
     return pantalla; 
}
class Punto
{
     friend
     ostream& operator << (ostream& pantalla, const Punto& mp);
     // ...
};

Una clase completa se puede hacer amiga de otra clase. De esta forma todas las funciones miembro de la clase amiga pueden acceder a lo miembros protegidos de la otra clase. Por ejemplo, la clase MandoDistancia se hace amiga de la clase Television:


class MandoDistancia { ... };
class Television
{
     friend class MandoDistancia;
     // ...
}

Regla:
La declaración de la amistad empieza por la palabra reservada friend, sólo pueden aparecer dentro de la declaración de una clase. Se pude situar en cualquier parte de la clase, es práctica recomendada agrupar todas las declaraciones friend inmediatamente a continuación de la cabecera de la clase. 



  • 2.8. TIPOS ABSTRACTOS DE DATOS EN C++
La estructura más adecuada, en C++, para implementación un TAD es la clase. Dentro de la clase va a residir la representación de los datos junto a las operaciones (funciones miembro de la clase). La interfaz del tipo abstracto queda perfectamente determinado con la etiqueta public, que se aplicará a las funciones de la clase que representen operaciones.
Por ejemplo si se ha especificado el TAD PuntoTres para representar la abstracción punto en el espacio tridimensional, la clase que implementa el tipo:

class PuntoTres
{
private:
     double x, y ,z;          // representación de los datos
public:                       // operaciones
     double distancia(PuntoTres p);
     double modulo();
     double anguloZeta();
     ...
};


  • 2.8.1. Implementación del TAD Conjunto
En el Apartado 1.6.2 se ha especificado el TAD Conjunto, la implementación se realiza con la clase Conjunto. Se supone que los elementos del conjunto son cadenas (string), aunque se podría generalizar creando una clase plantilla (template), pero se refiere simplificar el desarrollo y más adelante utilizar la genericidad.

La declaración de la clase está en el archivo Conjunto.h, la implementación de las funciones, la definición, en Conjunto.cpp. Los elementos del conjunto se guardan en un array que crece dinámicamente.

Archivo Conjunto.h

const int M = 20;
class Conjunto
{
private:          // representación
     string* cto;
     int cardinal;
     int capacidad;
public:          // operaciones
     Conjunto(); const;
     Conjunto (const Conjunto& org);
     bool esVacio() const;
     void annadir(string elemento);
     void retirar(string elemento) const;
     int cardinal() const;
     Conjunto union(const Conjunto& c2)
};

Archivo Conjunto.cpp
#include 
using namespace std;
#include "Conjunto.h"

Conjunto::Conjunto()
{
     cto = new string[M];
     cardinal = 0;
     capacidad = M;
}

Conjunto::Conjunto(const Conjunto& org)
{
     cardinal = org.cardinal;
     capacidad = org.capacidad;
     cto = new string[capacidad];     // copia segura
     for (int i = 0; i < cardinal; i++)
          cto[i] = opg.cto[i];
}

bool Conjunto()::esVacio() const
{
     return (cardinal == 0);
}

void Conjunto::annadir(string elemento)
{
    if (!pertenece(elemento))
    {
          // amplia el conjunto si no hay posiciones libres
         if (cardinal == capacidad)
          {
               string* nuevoCto;
               nuevoCto 0 new string[capacidad + M];
               for (int k=0; k<cardinal; k++)
                    nuevoCto[k] = cto[k];
               delete cto;
               cto = nuevoCto;
               capacidad += M;
          }
          cto[cardinal++] = elemento;
    }
}


void Conjunto::retirar(string elemento) const
{
    if (pertenece(elmento))
     {
          int k = 0;
          while (!(cto[k] == elemento)) k++;


          // mueve a la izquierda desde elemento k+1 hasta la última posición
          for (; k>cardinal; k++)
          cto[k] = cto[k+1];
          cardinal--;
     }   
}

bool Conjunto::pertenece(string elemento) const
{
     int k = 0;
     bool encontrado = false;

     while (k<cardinal && !encontrado)
     {
          encontrado = cto[k] == elemento;
          k++;
     }
     return encontrado;
}

int Conjunto::cardinal() const
{
     return this -> cardinal;
}

Conjunto Conjunto::union(const Conjunto& c2)
{
     int k;
     Conjunto u;     // conjunto unión
                     // primero copia el primer operando de la unión
     for (k=0; k<cardinal; k++)
     u.cto[k] = cto[k];
     u.cardinal = cardinal;
                     // añade los elementos  de c2 no incluidos
     for (k=0; k<c2.cardinal; k++)
         u.annadir(c2.cto[k]);
     return u;
}




sábado, 7 de junio de 2014

Página 11. Estructura de datos en C++

  • 2.2.6. Sobrecarga de funciones miembro

Al igual que sucede con las funciones no miembro de una clase, las funciones miembro de una clase se pueden sobrecargar. Una función miembro se puede sobrecargar pero sólo en su propia clase.

Las mismas reglas utilizadas para sobrecargar funciones ordinarias se aplican a las funciones miembro: dos miembros sobrecargados no puede tener el mismo número y tipo de parámetros. La sobrecarga permite utilizar un mismo nombre para una función y ejecutar la función definida más adecuada a los parámetros pasados durante la ejecución del programa. La ventaja fundamental de trabajar con funciones miembro sobrecargadas es la comodidad que aporta a la programación.

Para ilustrar la sobrecarga, veamos la clase Vector donde aparecen diferentes funciones miembro con el mismo nombre y diferentes tipos de parámetros.
class Vector
{
public:
     int suma(int m[], int n);     //funcion 1
     int suma(const Vector& v);    //funcion 2
     float suma(float m, float n)  //funcion 3
     int suma();                   //funcion 4
};

Normas para la sobrecarga
No pueden existir dos funciones en el mismo ámbito con igual signatura (lista de parámetros).


  • 2.3. CONSTRUCTORES Y DESTRUCTORES
Un constructor es una función miembro que se ejecuta automáticamente cuando se crea un objeto de una clase. Sirve para inicializar los miembros de la clase.

El constructor tiene el mismo nombre que la clase. Cuando se define no se puede especificar un valor de retorno, nunca devuelve un valor. Sin embargo, puede tomar cualquier número de argumentos.

Reglas
  1. El constructor tiene el mismo nombre que la clase.
  2. Puede tener cero, o más argumentos.
  3. No tiene tipo de retorno.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.4. La clase Rectángulo tiene un constructor con cuatro parámetros.

class Rectangulo
{
private:
     int izdo,superior;
     int dcha, inferior;
public:
     Rectangulo(int iz, int sr, int d, int inf)     //constructor
     {
          izdo = iz; superior = sr;
          dcha = d; inferior = inf;
     }
     //definiciones de otros métodos de miembro
};

Al crear un objeto se crean los valores al constructor, con la misma sintaxis que la de llamada a una función. Por ejemplo:

Rectangulo Rect(4, 4, 10, 10);
Rectangulo* ptr = new Rectangulo(25, 25, 75, 75);

Se han creado dos instancias de Rectangulo, pasando valores concretos al constructor de la clase.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 2.3.1. Constructor por defecto
Un constructor que no tiene parámetros, o que se puede llamar sin argumentos, se llama constructor por defecto. Un constructor por defecto normalmente inicializa los miembros dato de la clase con valores por defecto.

Regla
C++ crea automáticamente un constructor por defecto cuando no existen otros constructores: Tal constructor inicializa el objeto a ceros binarios. 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.5. El constructor de la clase Complejo tiene dos argumentos con valores por defecto 0 y 1 respectivamente, por tanto, puede llamarse sin argumentos.

class Complejo
{
private:
    double x;
    double y;
public:
     Complejo(double r = 0.0, double i = 1.0)
     {
          x = r;
          y = i;
     }
};

Cuando se crea un objeto Complejo puede inicializarse a los valores por defecto, o bien a otros valores.

Complejo z1;   // z1.x == 0.0, z1.y == 1.0
Complejo* pz = new Complejo(-2, 3);      // pz -> x == -2, pz -> y == 3

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 2.3.2. Constructores sobrecargados

Al igual que se puede sobrecargar un método de una clase, también se puede sobrecargar el constructor de una clase. De hecho los constructores sobrecargados son bastante frecuentes, proporcionan diferentes alternativas de inicializar objetos.

Regla
Para prevenir a los usuarios de la clase de crear un objeto sin parámetros, se puede: 
1) omitir el constructor por defecto; o bien, 2) hacer el constructor primario.


-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.6. La clase EquipoSonido se define con tres constructores; un constructor por defecto, otro con un argumento de tipo cadena y el tercero con tres argumentos.
class EquipoSonido
{
private:
     int potencia, voltios;
     string marca;
public:
     EquipoSonido()     //constructor por defecto
     {
          marca = "Sin marca"; potencia = voltios = 0;
     }
     EquipoSonido(string mt)
     {
          marca = mt; potencia = voltios = 0;
     }
     EquipoSonido(string mt, int p, int v)
     {
          marca = mt;
          potencia = p;
          voltios = v;
     }
};

La instanciación de un objeto EquipoSonido puede hacerse llamando a cualquier constructor. A continuación se crean tres objetos:

EquipoSonido rt;     // constructor por defecto
EquipoSonido ft("POLASIT");
EquipoSonido gt("PARTOLA", 35, 220); 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 2.3.3. Array de objetos

Los objetos se pueden estructurar como un array.Cuando se crea un array de objetos éstos se inicializan llamando al constructor sin argumentos, Por consiguiente, siempre que se prevea organizar los objetos en un array, la clase debe de tener un constructor que pueda llamarse sin parámetros.

Precaución
Tenga cuidado con la estructura de una clase con sólo un constructor con argumentos. Si se omite un constructor que pueda llamarse sin argumento no será posible crear un array de objetos.  

-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.7. Se crean arrays de objetos de tipo Complejo y EquipoSonido.

Complejo zz[10];      // crea 10 objetos, cada uno se inicializa a 0,1
EquipoSonido* pq;     // declaración de un puntero
int n;
cout << "Número de equipos: "; cin >> n;
pq = new EquipoSonido[n];

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 2.3.4. Constructor de copia

Este tipo de constructor se activa cuando al crear un objeto se inicializa con otro objeto de la misma clase. Por ejemplo:

Complejo z1(1, -3);     // z1 se inicializa con el constructor
Complejo z2 = z1;       // z2 se inicializa con z1, actúa el constructor de copia

También se llama al constructor de copia cuando se pasa un objeto por valor a una función, o bien cuando la función devuelve un objeto. Por ejemplo:

extern Complejo resultado(complejo d);

Para llamar a esta función se pasa un parámetro de tipo Complejo, un objeto. En esta transferencia se llama al constructor de copia. Lo mismo ocurre cuando la misma función devuelve (return) el resultado, un objeto de la clase Complejo

El constructor copia es una función miembro de la clase, su prototipo:

NombreClase(const NombreClase& origen);

El argumento origen es el objeto copiado, z1 en el primer ejemplo. La definición del constructor de copia para la clase complejo:

Complejo(const Complejo& origen)
{
     x = origen.x;
     y = origen.y;
}

No es siempre necesario definir el constructor de copia, por defecto se realiza una copia miembro a miembro. Sin embargo, cuando las clase tenga atributos (punteros) que presenten memoria dinámica, buffer dinámico, sí debe definirse, para realizar una copia segura, reservando memoria y copiando en esa memoria o buffer los elementos.

  • 2.3.5. Asignación de objetos

El operador de asignación, =, se puede utilizar con objetos, de igual forma que con datos de tipo simple. Por ejemplo:

Racional r1(1, 3);
Racional r2(2, 5);
r2 = r1;     // r2 toma los datos del objeto r1

Por defecto, la asignación se realiza miembro a miembro. El numerador de r2 toma el valor del numerador de r1 y el denominador de r2 el valor del denominador de r1.
C++ permite cambiar la forma de asignar objetos de una clase. Para  ello se implementa una función miembro de la clase especial(se denomina sobrecarga del operador =) con este prototipo:

nombreClase& operator = (const nombreClase&);

A continuación se implementa esta función en la clase Persona:

class Persona
{
private:
     int edad;
     string nom,apell;
     string dni;
public:
    // sobrecarga del operador de asignación
     if (this == &p) return *this; // es el mismo objeto
     edad = p.edad;
     nom = p.nom;
     apell = p.apell;
     dni = p.dni;
     return *this;
}

En esta definición se especifica que no se tome acción si el objeto que se asigna en el mismo: if (this == &p).
En la mayoría de las clases no es necesario definir el operador de asignación ya que, por defecto, se realiza una asignación miembro a miembro. Sin embargo, cuando la clase tenga miembros de tipo puntero (memoria dinámica, buffer dinámico) sí debe definirse, para que la asignación sea segura, reservando memoria y asignando a esa memoria o buffer los elementos.


-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.8. La clase Libro se define como un array dinámico de páginas(clase pagina).
El constructor establece el nombre del autor, el número de páginas y crea el array. Además, se escribe el constructor de copia del operador de asignación.
//archivo Libro.h

class libro
{
private:
     int numPags, inx;
     string autor;
     Pagina* pag;
public:
     Libro(string a, int n);     // constructor
     Libro(const Libro& cl);     // constructor de copia
     Libro& operator = (const Libro& al);     // operador de asignación
     // ... funciones miembro
};

// archivo Libro.cpp

Libro::Libro(string a, int n)
{
     autor = a;
     inx = 0;
     numPage = n;
     pag = new Pagina[numPags];
}

// constructor de copia
Libro::Libro(const Libro& cl)
{
     autor = cl.a;
     inx = cl.inx;
     numPags = cl.numPags;
     pag = new Pagina[numPags];     // copia segura
     for (int i = 0; i < inx; i++)
          pag[i] = cl.pag[i];
}

// operador de asignación
Libro& operator = (const Libro& al)
{
     if (this == &p) return *this;
     autor = al.a;
     inx = al.inx;
     numPags = al.numPags;
     pag = new Pagina[numPags];     // copia segura
     for (int i = 0; i < inx; i++)
          pag[i] = al.pag[i];
     return *this;
}


-------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 2.3.6. Destructor

Un objeto se libera, se destruye, cuando se sale del ámbito de definición. También, un objeto creado dinámicamente, con el operador  new, se libera al aplicar el operador delete al puntero que lo referencia. Por ejemplo:
Punto *p = new Punto(1, 2);
if(...)
{
     Punto p1(2, 1);
     Complejo z1(8, -9);
}     // los objetos p1 y z1 se destruyen
delete p;

El destructor tiene el mismo nombre que la clase, precedido de una tilde (~). Cuando se define, no se puede especificar un valor de retorno, ni argumentos:

~NombreClase()
{
     ;
}

El destructor es necesario implementarlo cuando el objeto contenga memoria reservada dinámicamente. 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
EJEMPLO 2.9. Se declara la clase  Equipo con dos atributos de tipo puntero, un constructor con valores por defecto y el destructor.

El constructor define un array de n objetos Jugador con el operador new. El destructor libera memoria reservada.
class Equipo
{
private:
     Jugador* jg;
     int numJug;
     int actual;
public:
     Equipo(int n = 12)
     {
          jg = new Jugador[n];
          numJug = n; 
          actual = 0;
     }
     ~Equipo()     // destructor
     {
          if (jg != 0) delete [] jg;
     }
}