Anuncios

Bienvenidos sean a este nuevo post, hoy continuaremos con lo empezado en el post anterior, hoy trataremos otros tipos de array y las llamadas listas enlazadas.

Anuncios

Hasta ahora vimos cadenas constantes sin nombre utilizados con el comando cout para mostrar un mensaje en pantalla pero se puede utilizar la cadena tipo char, por definicion propia en C++ una cadena es una array de valores tipo char  finalizados con un valor nulo. Veamos una comparacion:

cout << "Hola Mundo!\n";
Anuncios

Esto es llamado una cadena constante sin nombre, ahora vamos a ver una cadena tipo char y como se inicializa:

char Saludo[] = {'H','o','l','a',',',' ','M','u','n','d','o','!','\0'};
Anuncios

Esto parece demasiado complejo y en general tiende a crear muchos errores porque una simple distraccion nos generaria un inconveniente, por esto el compilador nos permite utilizar otra sintaxis:

char Saludo[] = "Hola, Mundo!";
Anuncios

Esta es una version mas practica y comoda pero vean la desaparicion de las llaves({ }) y las comillas simples (‘) para ser reemplazadas unicamente por unas comillas dobles («) al principio y al final, por el valor nulo no se preocupe porque el compilador se encargara de agregarlo por ud. dando como resultado un ejemplo valido de cadena tipo char, y al igual que los otros arrays se puede crear sin inicializar. Vamos a ver un ejemplo:

char00.cpp

# include <iostream>

using namespace std;

int main()
{
	char buffer[ 80 ];
 	cout << "Escriba una cadena: ";
 	cin >> buffer;
 	cout << "Aqui esta el buffer: " << buffer << endl;
 	return 0;
}
Anuncios

En este ejemplo creamos una cadena llamada buffer y le asignamos un tamaño de setenta y nueve caracteres y el valor nulo para terminarlo. Si utilizaramos este ejemplo vamos a tener dos errores, el primero es no tener una forma de controlar el exceso de los setenta y nueve caracteres y el segundo es ante la aparicion de algun espacio, esto se pueda considerar un valor nulo y termina con la cadena, compilen el programa y prueben de la siguiente forma:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./char00
Escriba una cadena: Hola, Mundo!
Aqui esta el buffer: Hola,
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

En este caso no nos devolvio todo el texto por encontrar un espacio en blanco, para evitar esto vamos a utilizar un metodo de cin llamado get(), este metodo esta compuesto de tres parametros:

  • La cadena utilizada para almacenar la informacion
  • El tamaño maximo de caracteres
  • El delimitador de la entrada, en este caso por defecto es newline.

Del ejemplo anterior modifiquemos la siguiente linea:

cin >> buffer;

Por la siguiente linea:

cin.get(buffer, 79); 
Anuncios

Con estas modificaciones realizadas podemos volverlo a compilar y probar nuevamente nuestro programa donde obtendremos la siguiente salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./char00 
Escriba una cadena: Hola, Mundo! 
Aqui esta el buffer: Hola, Mundo! 
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Con esta modificacion podemos observar que no nos discrimino el texto sino que tomo el mismo y lo almaceno en buffer, en este caso es un metodo de cin, esta unido por medio del punto y en el metodo get primero le indicamos el nombre de la cadena tipo char, separada por una coma el tamaño maximo y por ultimo deberia ir el delimitador pero en este caso no lo utilizamos porque vamos a usar el por defecto (newline), con esta modificacion como vimos anteriormente solucionamos nuestro mayor inconveniente que es el de ingresar un caracter que el char puede considerar un delimitador y tambien evitar que nos excedamos con el tamaño del array.

Anuncios

Pasemos a hablar de algunas funciones para las cadenas, las cuales fueron heredadas de C:

  • strcpy() // copia en un buffer designado todo el contenido de una cadena
  • strncpy() // copia el numero especificado de caracteres de una cadena a otra
  • strlen() // nos indica el tamaño de la cadena (no es el tamaño del array)
  • strcat() // concatena dos cadenas
Anuncios

Hagamos el siguiente ejemplo para ver el resultado de cada una de las funciones antes mencionadas:

char01.cpp

# include <string.h>
# include <iostream>

using namespace std;

int main()
{
 	char Cadena1[] = "Asi, siempre asi he de verte!";
 	char Cadena2[ 80 ] = "";
 	char Cadena3[ 80 ] = "";
	cout << "Cadena 1: " << Cadena1 << endl;
 	strcpy(Cadena2, Cadena1);
 	cout << "Cadena 2: " << Cadena2 << endl;
 	strncpy(Cadena3, Cadena1, 5);
 	Cadena3[ 5 ] = '\0';
 	cout << "Cadena 3 despues de strncpy: ";
 	cout << Cadena3 << endl;
 	cout << "Cadena 1 mide " << strlen(Cadena1);
	cout << " bytes de largo.\n";
 	cout << "Cadena 2 mide " << strlen(Cadena2); 
	cout << " bytes de largo.\n";
 	cout << "Y Cadena 3 mide " << strlen(Cadena3);
	cout << " bytes de largo." << endl;
 	strcat(Cadena3, Cadena1);
 	cout << "Cadena 3 despues de strcat: ";
 	cout << Cadena3 << endl;
 	cout << "Cadena 1 aun mide " << strlen(Cadena1);
	cout << " bytes de largo.\n";
 	cout << "Cadena 2 aun mide " << strlen(Cadena2);
	cout << " bytes de largo.\n";
 	cout << "y ahora Cadena3 mide " << strlen(Cadena3);
	cout << " bytes de largo.\n";
 	return 0;
}
Anuncios

Analicemos este ejemplo, el primer cambio a la vista es la inclusion a la libreria string.h, ademas de iostream, con esta nueva libreria podremos utilizar los cuatro comandos para manipular las cadenas. luego declaramos tres cadenas, Cadena1, Cadena2 y Cadena3, en la primera cadena la iniciamos con una frase, y las otras dos no las iniciamos pero a diferencia de la primera le pusimos una cantidad de caracteres. Iniciamos por mostrar el valor de Cadena1, despues empezamos la primera manipulacion en la cual vamos a hacer una copia del contenido de la Cadena1 en la Cadena2 con la funcion strcpy y lo mostramos, despues hicimos una copia parcial de la Cadena1 a la Cadena3 con strncopy, luego de realizar la accion anterior debemos agregar un caracter nulo despues de la parte copiada para tener una cadena valida, una vez hecho esto ejecutamos una accion para ver como quedo Cadena3, y a partir de aca usamos tres veces la funcion strlen para ver el tamaño de cada una de las cadenas en pantalla, una vez pasada las lineas utilizamos el strcat para concatenar la Cadena3 con la Cadena1, luego mostramos como quedo finalmente Cadena3, y por ultimo utilizamos nuevamente strlen para chequear que Cadena1 y Cadena2 no cambiaron de tamaño pero Cadena3 si, compilemos y probemos nuestro programa para obtener la siguiente salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./char01
Cadena 1: Asi, siempre asi he de verte!
Cadena 2: Asi, siempre asi he de verte!
Cadena 3 despues de strncpy: Asi,
Cadena 1 mide 29 bytes de largo.
Cadena 2 mide 29 bytes de largo.
Y Cadena 3 mide 5 bytes de largo.
Cadena 3 despues de strcat: Asi, Asi, siempre asi he de verte!
Cadena 1 aun mide 29 bytes de largo.
Cadena 2 aun mide 29 bytes de largo.
y ahora Cadena3 mide 34 bytes de largo.
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Asi como hay funciones tambien existen clases para cadenas, pero en gral. las heredadas de C no estan orientadas a objetos y terminaremos haciendo nuestras propias, en un proximo post lo hare pura y exclusivamente de clases de cadenas, por lo tanto prosigamos con otro tema: Listas enlazadas.

Anuncios

Como vimos hasta ahora los arrays son contenedores fijos de informacion y como vimos anteriormente en este post cuando se completan nos recortan la informacion en el caso de tener un tamaño asignado, y en el caso de no asignar ninguno puede quedar uno demasiado grande de manera innecesaria, para evitar esto se utilizan las listas enlazadas, hay de tres tipos de listas:

  • Listas con un solo enlace
  • Listas con doble enlace
  • Listas tipo arbol
Anuncios

Cada contenedor de una lista se llama nodo, el primer nodo se llama cabeza y el ultimo cola, hagamos una breve explicacion de las listas, en el caso de un solo enlace cada nodo apunta hacia adelante, es decir el proximo nodo, y nunca para el nodo anterior, en el caso de doble enlace vamos a tener un ida y vuelta entre los nodos permitiendo ir adelante y atras entre ellos, y para el arbol digamos, por ahora, es una estructura compleja entre nodos, para entender un poco mejor el concepto veamos el siguiente ejemplo:

lista00.cpp

# include <iostream>

using namespace std;

enum { kEsMasChico, kEsMasGrande, kEsIgual };

class Datos
{
public:
 	Datos(int val) : miValor(val) {}
 	~Datos(){}
 	int Comparar(const Datos &);
 	void Mostrar() { cout << miValor << endl; }
private:
 	int miValor;
};

int Datos :: Comparar(const Datos & losOtrosDatos)
{
 	if (miValor < losOtrosDatos.miValor)
 		return kEsMasChico;
 	else if (miValor > losOtrosDatos.miValor)
 		return kEsMasGrande;
 	else
 		return kEsIgual;
}

class Nodo
{
public:
 	Nodo(){}
 	virtual ~Nodo(){}
 	virtual Nodo * Insertar(Datos * losDatos) = 0;
 	virtual void Mostrar() = 0;
};

class NodoInterno : public Nodo
{
public:
 	NodoInterno(Datos * losDatos, Nodo * siguiente);
 	~NodoInterno() { delete miSiguiente; delete misDatos; }
 	virtual Nodo * Insertar(Datos * losDatos);
 	virtual void Mostrar() {
		misDatos -> Mostrar();
 		miSiguiente -> Mostrar();
 		}
private:
 	Datos * misDatos;
 	Nodo * miSiguiente;
};

NodoInterno :: NodoInterno(Datos * losDatos, Nodo * siguiente):
 	misDatos(losDatos),
 	miSiguiente(siguiente)
 	{}

Nodo * NodoInterno::Insertar(Datos * losDatos)
{
 	int resultado = misDatos -> Comparar(*losDatos);
 	switch(resultado)
 	{
 		case kEsIgual:
		case kEsMasGrande:
 		{
 			NodoInterno * nodoDatos = new NodoInterno
				(losDatos, this);
 			return nodoDatos;
 		}
 		case kEsMasChico:
 			miSiguiente = miSiguiente -> Insertar(losDatos);
 			return this;
 	}
 	return this;
}

class NodoCola : public Nodo
{
public:
 	NodoCola(){}
 	~NodoCola(){}
 	virtual Nodo * Insertar(Datos * losDatos);
 	virtual void Mostrar(){}
};

Nodo * NodoCola :: Insertar(Datos * losDatos)
{
 	NodoInterno * nodoDatos = new NodoInterno(losDatos, this);
 	return nodoDatos;
}
class NodoCabeza : public Nodo
{
public:
 	NodoCabeza();
 	~NodoCabeza() { delete miSiguiente; }
 	virtual Nodo * Insertar(Datos * losDatos);
 	virtual void Mostrar(){ miSiguiente -> Mostrar(); }
private:
 	Nodo * miSiguiente;
};

NodoCabeza :: NodoCabeza()
{
 	miSiguiente = new NodoCola;
}
Nodo * NodoCabeza :: Insertar(Datos * losDatos)
{
 	miSiguiente = miSiguiente->Insertar(losDatos);
 	return this;
}

class ListaEnlazada
{
public:
 	ListaEnlazada();
 	~ListaEnlazada(){ delete miCabeza; }
 	void Insertar(Datos * losDatos);
 	void MostrarTodo(){ miCabeza->Mostrar(); }
private:
 	NodoCabeza * miCabeza;
};

ListaEnlazada::ListaEnlazada()
{
 	miCabeza = new NodoCabeza;
}

void ListaEnlazada::Insertar(Datos * apDatos)
{
 	miCabeza->Insertar(apDatos);
}

int main()
{
 	Datos * apDatos;
 	int val;
 	ListaEnlazada le;
	for(;;)
 	{
 		cout << "Cual valor? (0 para detener): ";
 		cin >> val;
 		if (!val)
 			break;
 		apDatos = new Datos(val);
 		le.Insertar(apDatos);
 	}
 	le.MostrarTodo();
 	return 0;
}
Anuncios

Analicemos este ejemplo, primero crearemos una clase llamada Datos la cual contendra los metodos Comparar() y Mostrar() para poder trabajar con nuestras listas, Mostrar solamente mostrara en pantalla el contenido de miValor, veamos la funcion Comparar():

int Datos :: Comparar(const Datos & losOtrosDatos)
{
 	if (miValor < losOtrosDatos.miValor)
 		return kEsMasChico;
 	else if (miValor > losOtrosDatos.miValor)
 		return kEsMasGrande;
 	else
 		return kEsIgual;
}
Anuncios

En este caso tendra tres condicionales y cada condicion devolvera uno de los constantes creados al principio, despues tendremos una clase llamada Nodo la cual servira como clase base y observen que los dos metodos de la clase (Insertar y Mostrar) le agregamos el valor igual a cero para simbolizar que son abstractas y estan deberan ser redefinidas en las clases herederas de esta, nuestra siguiente clase se llama NodoInterno la cual es heredera de Nodo, dentro de esta creamos el prototipo de Insertar y Mostrar utilizar la funcion Mostrar de la clase Datos, esto es gracias a que los objetos misDatos y miSiguiente son de la clase Datos y podemos acceder a dicho metodo, en el constructor inicializaremos los objetos de dicha clase con los valores enviados al mismo, luego definiremos a Insertar, en este caso creamos un variable llamada resultado y para esta utilizaremos el metodo Comparar con el valor informado en los atributos, dependiendo del valor enviado Comparar nos devolvera una de las tres constantes, donde despues por medio de un switch chequeara cual es la constante recibida, veamos este bloque:

 	switch(resultado)
 	{
 		case kEsIgual:
		case kEsMasGrande:
 		{
 			NodoInterno * nodoDatos = new NodoInterno
				(losDatos, this);
 			return nodoDatos;
 		}
 		case kEsMasChico:
 			miSiguiente = miSiguiente -> Insertar(losDatos);
 			return this;
 	}
Anuncios

Si nos devuelve kEsIgual no toma ninguna accion, en caso de kEsMasGrande genera un nuevo objeto llamado nodoDatos del tipo NodoInterno a lo cual le enviara el valor de losDatos, luego devuelve este objeto, en caso de la constante kEsMasChico a miSiguiente le inserta el valor de losDatos por medio de Insertar y retorna este valor por medio de this, salido del switch nos devuelve el valor de this, luego crearemos una clase llamada NodoCola tambien heredera de Nodo, en esta tambien redefiniremos los dos metodos, en el caso de Insertar usamos un prototipo pero en el caso de mostrar lo redefinimos sin ninguna instruccion simplemente para que no nos devuelve ningun error.

Anuncios

El metodo Insertar crea un nuevo objeto nodoDatos de tipo NodoInterno con los datos informados y luego lo retorna por medio de this, nuestra siguiente clase se llama NodoCabeza donde la haremos heredera de la clase Nodo y redefiniremos los metodos nuevamente, al igual que en el caso anterior creamos un prototipo de Insertar y en el caso de Mostrar utilizamos el metodo de la clase Datos, la unica diferencia es que el apuntador miSiguiente es de tipo Nodo en la parte privada de la clase, la funcion Insertar recibe un valor y este lo envia por medio del Insertar de la clase Datos devolviendolo por medio del this, nuestra ultima clase sera ListaEnlazada.

Anuncios

La misma no sera heredera de Nodo pero tendra los metodos Insertar y MostrarTodo, como en los casos anteriores creamos el prototipo de Insertar y en MostrarTodo usamos el metodo Mostrar de NodoCabeza, despues definiremos el constructor de la clase y para miSiguiente creara un nuevo objeto de NodoCola, en Insertar usamos el metodo Insertar de Datos y luego lo devolvemos por medio de this, con todo esto visto pasemos al cuerpo de main, primero crearemos un apuntador de tipo Datos llamado apDatos, luego una variable de tipo int llamada val y otro objeto llamado le de tipo ListaEnlazada, despues tendremos un bucle for de tipo infinito en la cual le pediremos al usuario que ingrese valores distintos de cero, el cual sera nuestro medio para salir del mismo, podemos ver que existe un condicional donde verificar que val sea distinta de un valor, consideremos vacio o cero, y en caso de ser verdadero ejecutara un break y saldra del bucle, la siguiente linea asignara el nuevo valor de val al apuntador apDatos, despues por medio de le insertaremos dicho valor a la lista, volviendo a repetir el ciclo, una vez fuera del ciclo procedemos a mostrar la lista en el orden que establecimos por medio de Comparar, si lo compilamos y probamos tendremos la siguiente salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./array05
Cual valor? (0 para detener): 1
Cual valor? (0 para detener): 5
Cual valor? (0 para detener): 15
Cual valor? (0 para detener): 10
Cual valor? (0 para detener): 4
Cual valor? (0 para detener): 7
Cual valor? (0 para detener): 2
Cual valor? (0 para detener): 0
1
2
4
5
7
10
15
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Como podran observar en la salida, nosotros podemos ingresar los valores en el orden que deseemos pero por medio de Comparar en la clase Datos e Insertar en la clase NodoInterno esto se iran ordenando de menor a mayor a medida que los vayamos ingresando.

Anuncios

En resumen, hoy hemos terminado con los arrays, hemos visto los array de caracteres que son similares a los tipo Cadena (String) de otros lenguajes, aunque este tambien lo posee, hemos podido utilizar otras herramientas para trabajar con las mismas, hemos visto listas enlazadas, hemos hablado muy por arriba de ellas, y hemos realizado un ejemplo practico para estudiarlas, 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.50

Anuncio publicitario