Anuncios

Bienvenidos sean a este nuevo post, hasta hoy hemos visto como las variables pueden ser locales y globales, apuntadores y referencias para variables. funciones y clases, tambien como las funciones y clases pueden intercambiar variables entre si pero hasta ahora no hemos visto una variable compartida entre todos las clases y/o funciones, estos son los datos miembro estaticos.

Anuncios

Como dijimos hasta ahora solo vimos a los datos de cada objeto pertenecen a ese objeto y no se comparten pero en algunas circunstancias necesitaremos saber, por ejemplo cuantos objetos de una clase se crearon y cuantos existen todavia, para entender un poco mejor este concepto pasemos al siguiente ejemplo:

static00.cpp

# include <iostream>

using namespace std;

class Gato
{
public:
 	Gato(int edad): suEdad(edad){ CuantosGatos++; }
 	virtual ~Gato() { CuantosGatos--; }
 	virtual int ObtenerEdad() { return suEdad; }
 	virtual void AsignarEdad(int edad) { suEdad = edad; }
 	static int CuantosGatos;
private:
 	int suEdad;
};

int Gato::CuantosGatos = 0;

int main()
{
 	const int MaxGatos = 5;
 	int i;
 	Gato *CasaGatos[MaxGatos];
 	for(i = 0; i < MaxGatos; i++)
 		CasaGatos[i] = new Gato(i);
	for(i = 0; i < MaxGatos; i++)
 	{
 		cout << "Quedan ";
 		cout << Gato::CuantosGatos;
 		cout << " gatos!\n";
 		cout << "Se va a eliminar el que tiene ";
 		cout << CasaGatos[i]->ObtenerEdad();
 		cout << " años de edad\n";
 		delete CasaGatos[i];
 		CasaGatos[i] = 0;
 	}
 	return 0;
}
Anuncios

En este ejemplo creamos una clase llamada Gato, ahi tendremos nuestro constructor y destructor y unos metodos para la clase pero agregamos esta linea nueva:

static int CuantosGatos;
Anuncios

Esta linea creara una variable de tipo estatica, esta se encargara de almacenar la cantidad de veces que se crea al objeto Gato.

Nota: Para una correcta inicializacion de la variable estatica se le debe asignar un valor antes del main, en este caso asignamos el valor de cero pero puede ser cualquier otro.
Anuncios

Si se fijaron en el constructor por cada gato creado, se le asignara un valor propio a la variable suEdad de cada gato pero tambien incrementaremos el valor de la variable estatica (CuantosGatos), ahora en el destructor decrementaremos el valor de CuantosGatos. Una vez vista la clase, pasemos a la siguiente linea donde se inicializa a CuantosGatos, si esto no se hace al compilarlo tendremos un error. Despues en el main(), un objeto Array de tipo Gato llamado CasaGatos, el cual tendra como limite a la constante MaxGatos, despues vamos a tener dos bucles for. El primero se encargara de crear un nuevo “aparcamiento” para Gato, en nuestro objeto CasaGatos, y dentro guardara los datos de cada uno de los Gatos e i le asignara el valor de la edad. Una vez hecho esto procederemos al siguiente bucle for, este se encargara de mostrar la edad de cada gato y cuantos gatos son eliminados porque en cada vuelta se ejecuta el delete y a la posicion correspondiente de CasaGatos le asignamos el valor de cero. Si todo sale bien deberian tener una salida similar a esta:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./static00
Quedan 5 gatos!
Se va a eliminar el que tiene 0 años de edad
Quedan 4 gatos!
Se va a eliminar el que tiene 1 años de edad
Quedan 3 gatos!
Se va a eliminar el que tiene 2 años de edad
Quedan 2 gatos!
Se va a eliminar el que tiene 3 años de edad
Quedan 1 gatos!
Se va a eliminar el que tiene 4 años de edad
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Aca se ve como la variable estatica CuantosGatos se encargo de almacenar la cantidad de veces que el objeto fue llamado, recuerden como el constructor lo incrementaba y con el destructor lo disminuimos por lo tanto en el primer bucle for lo incrementamos y en el segundo bucle lo disminuimos a continuacion pasaremos a un ejemplo similar al anterior pero donde el trabajo lo mostraremos a traves de una funcion:

static01.cpp

# include <iostream>

using namespace std;

class Gato
{
public:
 	Gato(int edad): suEdad(edad) { CuantosGatos++; }
 	virtual ~Gato() { CuantosGatos--; }
 	virtual int ObtenerEdad() { return suEdad; }
 	virtual void AsignarEdad(int edad) { suEdad = edad; }
 	static int CuantosGatos;
private:
 	int suEdad;
};

int Gato::CuantosGatos = 0;

void FuncionTelepatica();

int main()
{
 	const int MaxGatos = 5; int i;
 	Gato *CasaGatos[MaxGatos];

	for(i = 0; i < MaxGatos; i++)
 	{
 		CasaGatos[i] = new Gato(i);
 		FuncionTelepatica();
 	}

	for(i = 0; i < MaxGatos; i++)
 	{
 		delete CasaGatos[i];
 		FuncionTelepatica();
 	}

	return 0;
}

void FuncionTelepatica()
{
 	cout << "Hay ";
 	cout << Gato::CuantosGatos << " gatos vivos!\n";
}
Anuncios

Este ejemplo es muy similar al anterior pero cual es la diferencia? Si observan en este codigo tenemos creado un prototipo de una funcion llamada FuncionTelepatica(), en el resto sigue igual con la clase Gato, con su variable estatica llamada CuantosGatos, donde se incrementa y disminuye con el constructor y destructor respectivamente. Se inicia el mismo por fuera de la clase, como en el ejemplo anterior, pero la variacion va estar en los bucles for del cuerpo main(). En el primero volvemos a almacenar una referencia a un objeto Gato dentro de la variable CasaGatos y ahora le agregamos un llamado a la FuncionTelepatica(), y en el segundo bucle for podemos ver como se elimina el objeto y como volvemos a llamar la funcion FuncionTelepatica(). Como notaran en ambos casos  desaparecieron todas las lineas de cout y fueron reemplazadas por la funcion FuncionTelepatica(), ahora hablemos sobre la misma, en este caso solamente nos notificara la cantidad de gatos vivos, es decir la cantidad almacenada en nuestra variable estatica, en el caso del primer bucle nos llenara la variable y en el segundo bucle nos lo vaciara, vean la salida de ejemplo:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./static01
Hay 1 gatos vivos!
Hay 2 gatos vivos!
Hay 3 gatos vivos!
Hay 4 gatos vivos!
Hay 5 gatos vivos!
Hay 4 gatos vivos!
Hay 3 gatos vivos!
Hay 2 gatos vivos!
Hay 1 gatos vivos!
Hay 0 gatos vivos!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Como pueden ver, la primera mitad es la del primer bucle for donde incrementamos la cantidad de gatos y una vez finalizado empieza a descender por medio del segundo bucle for, para nuestro siguiente ejemplo pasaremos a ver como es una variable estatica privada porque en el caso anterior fue publica:

static02.cpp

# include <iostream>

using namespace std;

class Gato
{
public:
 	Gato(int edad): suEdad(edad) { CuantosGatos++; }
 	virtual ~Gato() { CuantosGatos--; }
 	virtual int ObtenerEdad() { return suEdad; }
 	virtual void AsignarEdad(int edad) { suEdad = edad; }
 	virtual int ObtenerCuantos() { return CuantosGatos; }
private:
 	int suEdad;
 	static int CuantosGatos;
};

int Gato::CuantosGatos = 0;

int main()
{
 	const int MaxGatos = 5; int i;
 	Gato *CasaGatos[MaxGatos];
 	for(i = 0; i < MaxGatos; i++)
 		CasaGatos[i] = new Gato(i);
	for(i = 0; i < MaxGatos; i++) 
	{
 		cout << "Quedan ";
 		cout << CasaGatos[i]->ObtenerCuantos();
 		cout << " gatos\n";
 		cout << "Se va a eliminar el que tiene ";
 		cout << CasaGatos[i]->ObtenerEdad()+2;
 		cout << " años de edad\n";
 		delete CasaGatos[i];
 		CasaGatos[i] = 0;
 	}
 	return 0;
}
Anuncios

Este ejemplo es similar a los anteriores pero la diferencia radica en la forma como tratamos a nuestra variable estatica, en lugar de ser publica sera privada. Esto implicara el no poder acceder directamente a la variable sino por medio de un metodo propio del objeto (clase), en este caso haremos un metodo llamado ObtenerCuantos() la cual se encargara unicamente de devolvernos el valor de CuantosGatos porque como en los otros ejemplos el constructor y el destructor son los encargados de modificarlo, eso pueden verlo en el main(), el primer bucle creara los objetos dentro del array CasaGatos y en el segundo bucle primero obtendremos la cantidad de gatos creados por medio del metodo ObtenerCuantos() para luego tambien obtener la edad con el metodo ObtenerEdad() y a la cual le sumamos dos, recuerden cuando creamos el nuevo objeto dentro de CasaGatos ahi tambien definimos la edad en base a la variable i del for y por ultimo vamos eliminando los gatos uno por uno, la salida es algo asi:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./static02
Quedan 5 gatos
Se va a eliminar el que tiene 2 años de edad
Quedan 4 gatos
Se va a eliminar el que tiene 3 años de edad
Quedan 3 gatos
Se va a eliminar el que tiene 4 años de edad
Quedan 2 gatos
Se va a eliminar el que tiene 5 años de edad
Quedan 1 gatos
Se va a eliminar el que tiene 6 años de edad
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Como pueden ver en la primera linea nos devuelve la cantidad obtenida de ObtenerCuantos() y en la segunda linea proveniente de ObtenerEdad() + 2. Hasta aqui hemos visto como tener una variable en comun para todos nuestros objetos creados, en nuestro siguiente ejemple veremos como es una funcion miembro estatica:

static03.cpp

# include <iostream>

using namespace std;

class Gato
{
public:
 	Gato(int edad): suEdad(edad) { CuantosGatos++; }
 	virtual ~Gato() { CuantosGatos--; }
 	virtual int ObtenerEdad() { return suEdad; }
 	virtual void AsignarEdad(int edad) { suEdad = edad; }
 	static int ObtenerCuantos() { return CuantosGatos; }
private:
 	int suEdad;
 	static int CuantosGatos;
};

int Gato::CuantosGatos = 0;

void FuncionTelepatica();

int main()
{
 	const int MaxGatos = 5;
 	int i;
 	Gato *CasaGatos[MaxGatos];
	for(i = 0; i < MaxGatos; i++)
 	{
 		CasaGatos[i] = new Gato(i);
 		FuncionTelepatica();
 	}
	for(i = 0; i < MaxGatos; i++)
 	{
 		delete CasaGatos[i];
 		FuncionTelepatica();
 	}
 	return 0;
}

void FuncionTelepatica()
{
 	cout << "Hay " << Gato::ObtenerCuantos() << " gatos vivos!!\n";
}
Anuncios

Este ejemplo es muy similar al segundo ejemplo donde usamos la funcion FuncionTelepatica() pero como pueden observar esta vez declaramos a nuestro metodo ObtenerCuantos() como estatico, lo cual implica poder ser utilizado sin necesidad de un objeto, el resto sigue igual al segundo ejemplo y al ejemplo anterior donde utilizamos de nuevo a CuantosGatos como una variable estatica privada, despues es igual al segundo ejemplo donde la unica diferencia estara en la funcion FuncionTelepatica() donde llamaremos al metodo directamente desde la clase pero esto no quita de no poder ser llamado desde un objeto, por ejemplo podriamos crear un objeto de Gato llamado elGato y desde ese objeto llamar al metodo de la siguiente forma: elGato.ObtenerCuantos(), la salida es exactamente igual al segundo ejemplo. para nuestro siguiente ejemplo estudiraremos a los apuntadores de funciones:

static04.cpp

# include <iostream>

using namespace std;

void Cuadrado(int &, int &);
void Cubo(int &, int &);
void Intercambiar(int &, int &);
void ObtenerValores(int &, int &);
void ImprimirValores(int &, int &);

int main()
{
 	void (* apFunc) (int &, int &);
 	bool fSalir = false;
	int valUno = 1, valDos = 2;
 	int opcion;
	while(fSalir == false)
 	{
 		cout << "(0)Salir (1)Cambiar Valores ";
		cout << "(2)Cuadrado (3)Cubo (4)Intercambiar: ";
 		cin >> opcion;
		switch(opcion)
 		{
 			case 1: apFunc = ObtenerValores; break;
 			case 2: apFunc = Cuadrado; break;
 			case 3: apFunc = Cubo; break;
 			case 4: apFunc = Intercambiar; break;
 			default: fSalir = true; break;
 		}
		if (fSalir)
 			break;
		ImprimirValores(valUno, valDos);
 		apFunc(valUno, valDos);
 		ImprimirValores(valUno, valDos);
 	}
 	return 0;
}

void ImprimirValores(int & x, int & y)
{
 	cout << "x: " << x << " y: " << y << endl;
}

void Cuadrado(int & rX, int & rY)
{
 	rX *= rX;
 	rY *= rY;
}

void Cubo(int & rX, int & rY)
{
 	int tmp;
	tmp = rX;
 	rX *= rX;
 	rX = rX * tmp;
	tmp = rY;
 	rY *= rY;
 	rY = rY * tmp;
}

void Intercambiar(int & rX, int & rY)
{
 	int temp;
 	temp = rX;
 	rX = rY;
 	rY = temp;
}

void ObtenerValores(int & rValUno, int & rValDos)
{
 	cout << "Nuevo valor para valUno: ";
 	cin >> rValUno;
 	cout << "Nuevo valor para valDos: ";
 	cin >> rValDos;
}
Anuncios

En este ejemplo vamos a ver como funciona un apuntador de funciones, esto es de lo mas util cuando terminemos el ejemplo les mostrare lo practico de esta funcionalidad. Primero crearemos nuestros prototipos de las funciones a utilizar, estas van a ser las siguientes:

  • Cuadrado
  • Cubo
  • Intercambiar
  • ObtenerValores
  • ImprimirValores
Anuncios

Todas ellas van a ser del tipo void con dos referencias a variables del tipo integer (int), es fundamental para cuando declaremos a nuestro apuntador de funciones. Una vez hecho los prototipos, pasaremos al cuerpo del main(), la primera linea va a crear el apuntador de nuestras funciones:

void (* apFunc) (int &, int &);
Anuncios

Primero, el tipo debe coincidir con el tipo de la funcion, en segundo crearemos un nombre para el apuntador y en este caso le transferiremos dos referencias de tipo enteras (int), como se observa todas las funciones son del tipo void y estan acompañadas con dos variables de tipo int, lo cual implicara el uso de este apuntador para todas nuestras funciones, la sintaxis es como se ve en la linea, siguiendo con el cuerpo del main(), definiremos nuestras variables a utilizar en el programa, despues utilizaremos un bucle while donde estara en un bucle infinito siempre y cuando fSalir sea igual a false, en el cuerpo del while escribiremos la primera linea mostrando las opciones disponibles, luego le pediremos la opcion deseada, luego crearemos un switch para chequear el valor de la variable opcion, en el caso de ser uno asignaremos ObtenerValores al apuntador apFunc, en el caso de dos asignara Cuadrado a apFunc, y asi con el resto, en el default, en este caso equivaldria a cero, asignara el valor true a fSalir para salir del programa. Volviendo al apuntador apFunc, en este caso no habria ningun inconveniente porque al ser funciones distintas estaran ubicadas en distintas secciones de la memoria pero lo bueno es que al ser llamada se utilizara la funcion asignada.

Anuncios

Luego del switch chequeara si fSalir es true, en caso de ser verdadera sale del ciclo con un break lo cual finaliza el programa, de lo contrario sigue a las siguientes tres lineas, primero imprimiremos los valores a traves de la funcion ImprimirValores(), luego llamamos al apuntador apFunc y ejecutara la funcion almacenada, para luego imprimir los valores modificados con la funcion ImprimirValores(), una vez terminado el ciclo hacemos el return 0 para salir del programa, cerrado el cuerpo del main() tenemos las funciones para ejecutar nuestras tareas:

  • ImprimirValores, imprime los valores asignados a valUno y valDos por medio de los dos valores pasador a traves del llamador.
  • Cuadrado, simplemente multiplica entre si los valores x e y pasados a traves de la llamada a la funcion y asignarlos a los apuntadores de valUno y valDos.
  • Cubo, este es un poco mas complejo porque deberemos crear una variable temporal (tmp) donde almacenaremos el valor de x o y, hacemos el cuadrado y a ese resultado lo volvamos a multiplicar por tmp para obtener el cubo.
  • Intercambiar, tambien mediante una variable temporal (temp) guarde el valor de x, asignamos a x el valor de y, luego el valor de temp a y, este tiene almacenado el valor de x.
  • ObtenerValores, con esta funcion asignaremos nuevos valores a valUno y valDos.
Anuncios

Como pueden ver lo interesante es llamar a las funciones a traves de un apuntador donde fue asignada, por ejemplo si nosotros no lo hicieramos a traves de un apuntador el programa anterior deberiamos modificar los case del switch de la siguiente forma por ejemplo:

case 1:
      ImprimirValores(valUno, valDos);
      ObtenerValores(valUno, valDos);
      ImprimirValores(valUno, valDos);
      break;
Anuncios

El programa en si es exactamente el mismo pero obviamente no tendremos nuestra linea para crear el apuntador pero en cada case deberiamos utilizar esas tres lineas, la primera para imprimir los valores, la relacionada al case, y nuevamente imprimir los valores, imaginense eso en cada case, nos incrementaria notablemente el tamaño de nuestro codigo fuente, seguramente sera mas facil de entender pero la funcion del apuntador es justamente reducirnos ese trabajo porque el uso de los apuntadores para funciones pueden ser herramientas muy utiles como hemos visto en otros casos, compilemos y probemos nuestro programa:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./static04
(0)Salir (1)Cambiar Valores (2)Cuadrado (3)Cubo (4)Intercambiar: 1
x: 1 y: 2
Nuevo valor para valUno: 2
Nuevo valor para valDos: 4
x: 2 y: 4
(0)Salir (1)Cambiar Valores (2)Cuadrado (3)Cubo (4)Intercambiar: 2
x: 2 y: 4
x: 4 y: 16
(0)Salir (1)Cambiar Valores (2)Cuadrado (3)Cubo (4)Intercambiar: 3
x: 4 y: 16
x: 64 y: 4096
(0)Salir (1)Cambiar Valores (2)Cuadrado (3)Cubo (4)Intercambiar: 4
x: 64 y: 4096
x: 4096 y: 64
(0)Salir (1)Cambiar Valores (2)Cuadrado (3)Cubo (4)Intercambiar: 0
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

En resumen, hoy hemos vistos las variables estaticas, a diferencia del resto de las variables de la clase donde sus valores pertenecen a cada uno de los objetos creados esta va a estar compartida por todos los objetos creados en base a la clase, las variables estaticas las podemos declarar como publicas o privadas, en el primer caso la variable va a estar accesible para todos lo objetos creados desde esta clase, en el segundo caso solamente podremos acceder si utilizamos algun metodo para acceder a ellos, tambien hemos visto como utilizar una funcion para utilizar nuestra variable estatica, hemos visto como hacer una funcion miembro estatica la cual nos permitira poder utilizarla sin la necesidad de la creacion de un objeto para utilizarla y por ultimo como crear un apuntador de memoria para asignarle funciones y crearnos varios atajos a las mismas, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00