Curso de C++ v2.0
Consultas, lista de correo 'C++ Con Clase' 'C++ Con Clase' página de entrada Librerías estándar C Tabla de contenido Contactar con Webmaster
*Introducción
*1 Toma de contacto
*2 Variables I
*3 Funciones I: Declaración y definición
*4 Operadores I
*5 Sentencias
*6 Declaración de variables
*7 Normas para la notación
*8 Cadenas de caracteres
*9 Conversión de tipos
*10 Variables II: Arrays
*11 Variables III: Estructuras
 . Funciones miembro
 . Inicialización
 . Asignación
 . Arrays de estructuras
 . Estructuras anidadas
 . Estructuras anónimas
 . sizeof con estructuras
 . Campos de bits
 . Problemas
 . Ejercicios capítulo 11
*12 Variables IV: Punteros 1
*13 Operadores II: Más operadores
*14 Operadores III: Precedencia
*15 Funciones II: Parámetros por valor y referencia
*16 Variables V: Uniones
*17 Variables VI: Punteros 2
*18 Operadores IV: De bits y condicional
*19 Definición de tipos
*20 Funciones III
*21 Funciones IV: Sobrecarga
*22 Operadores V: Sobrecarga
*23 El preprocesador
*24 Funciones V: Recursividad
*25 Variables VII: Modificadores
*26 Espacios con nombre
*27 Clases I: Definiciones
*28 Declaración de clases
*29 Constructores
*30 Destructores
*31 El puntero this
*32 Sistema de protección
*33 Modificadores para miembros
*34 Más sobre funciones
*35 Operadores sobrecargados
*36 Herencia
*37 Funciones virtuales
*38 Derivación múltiple
*39 Trabajar con ficheros
*40 Plantillas
*41 Punteros a miembros
*42 Castings
*43 Excepciones
*Ejemplos capítulos 1 a 6
*Ejemplos capítulos 8 y 9
*A Palabras reservadas C/C++
*B Trigrafos y símbolos alternativos
*C Librerías estándar
*D Streams
  > >>

11 Tipos de variables III: Estructuras

Las estructuras son el segundo tipo de datos estructurados que veremos.

Las estructuras nos permiten agrupar varios datos, aunque sean de distinto tipo, que mantengan algún tipo de relación, permitiendo manipularlos todos juntos, con un mismo identificador, o por separado.

Las estructuras son llamadas también muy a menudo registros, o en inglés "records". Y son estructuras análogas en muchos aspectos a los registros de bases de datos. Y siguiendo la misma analogía, cada variable de una estructura se denomina a menudo campo, o "field".

Sintaxis:

struct [<identificador>] {
   [<tipo> <nombre_variable>[,<nombre_variable>,...]];
   .
} [<variable_estructura>[,<variable_estructura>,...];

El nombre de la estructura es un nombre opcional para referirse a la estructura.

Las variables de estructura son variables declaradas del tipo de la estructura, y su inclusión también es opcional. Sin bien, al menos uno de estos elementos debe existir, aunque ambos sean opcionales.

En el interior de una estructura, entre las llaves, se pueden definir todos los elementos que consideremos necesarios, del mismo modo que se declaran las variables.

Las estructuras pueden referenciarse completas, usando su nombre, como hacemos con las variables que ya conocemos, y también se puede acceder a los elementos en el interior de la estructura usando el operador de selección (.), un punto.

También pueden declararse más variables del tipo de estructura en cualquier parte del programa, de la siguiente forma:

[struct] <identificador> <variable_estructura>
   [,<variable_estructura>...];

En C++ la palabra "struct" es opcional en la declaración de variables. En C es obligatorio usarla.

Ejemplo:

struct Persona {
   char Nombre[65]; 
   char Direccion[65]; 
   int AnyoNacimiento; 
} Fulanito; 

Este ejemplo declara a Fulanito como una variable de tipo Persona. Para acceder al nombre de Fulanito, por ejemplo para visualizarlo, usaremos la forma:

cout << Fulanito.Nombre; 

Funciones en el interior de estructuras:   

C++, al contrario que C, permite incluir funciones en el interior de las estructuras. Normalmente estas funciones tienen la misión de manipular los datos incluidos en la estructura.

Aunque esta característica se usa casi exclusivamente con las clases, como veremos más adelante, también puede usarse en las estructuras.

Dos funciones muy particulares son las de inicialización, o constructor, y el destructor. Veremos con más detalle estas funciones cuando asociemos las estructuras y los punteros.

El constructor es una función sin tipo de retorno y con el mismo nombre que la estructura. El destructor tiene la misma forma, salvo que el nombre va precedido el operador "~".

Nota: para aquellos que usen un teclado español, el símbolo "~" se obtiene pulsando las teclas del teclado numérico 1, 2, 6, mientras se mantiene pulsada la tecla ALT, ([ALT]+126). También mediante la combinación [Atl Gr]+[4] (la tecla [4] de la zona de las letras, no del teclado numérico).

Veamos un ejemplo sencillo para ilustrar el uso de constructores:

Forma 1:

struct Punto { 
   int x, y; 
   Punto() {x = 0; y = 0;} // Constructor
} Punto1, Punto2; 

Forma 2:

struct Punto { 
   int x, y; 
   Punto(); // Declaración del constructor
} Punto1, Punto2;
 
// Definición del constructor, fuera de la estructura
Punto::Punto() {  
   x = 0; 
   y = 0; 
} 

Si no usáramos un constructor, los valores de x e y para Punto1 y Punto2 estarían indeterminados, contendrían la "basura" que hubiese en la memoria asignada a estas estructuras durante la ejecución. Con las estructuras éste será el caso más habitual.

Mencionar aquí, sólo a título de información, que el constructor no tiene por qué ser único. Se pueden incluir varios constructores, pero veremos esto mucho mejor y con más detalle cuando veamos las clases.

Usando constructores nos aseguramos los valores iniciales para los elementos de la estructura. Veremos que esto puede ser una gran ventaja, sobre todo cuando combinemos estructuras con punteros, en capítulos posteriores.

También podemos incluir otras funciones, que se declaran y definen como las funciones que ya conocemos, salvo que tienen restringido su ámbito al interior de la estructura.

Otro ejemplo:

#include <iostream>
using namespace std;

struct stPareja { 
   int A, B; 
   int LeeA() { return A;} // Devuelve el valor de A
   int LeeB() { return B;} // Devuelve el valor de B
   void GuardaA(int n) { A = n;} // Asigna un nuevo valor a A
   void GuardaB(int n) { B = n;} // Asigna un nuevo valor a B
} Par;
 
int main() { 
   Par.GuardaA(15); 
   Par.GuardaB(63); 
   cout << Par.LeeA() << endl; 
   cout << Par.LeeB() << endl;
 
   cin.get();
   return 0; 
}

En este ejemplo podemos ver cómo se define una estructura con dos campos enteros, y dos funciones para modificar y leer sus valores. El ejemplo es muy simple, pero las funciones de guardar valores se pueden elaborar para que no permitan determinados valores, o para que hagan algún tratamiento de los datos.

Por supuesto se pueden definir otras funciones y también constructores más elaborados y sobrecarga de operadores. Y en general, las estructuras admiten cualquiera de las características de las clases, siendo en muchos aspectos equivalentes.

Veremos estas características cuando estudiemos las clases, y recordaremos cómo aplicarlas a las estructuras.

Inicialización de estructuras:  

De un modo parecido al que se inicializan los arrays, se pueden inicializar estructuras, tan sólo hay que tener cuidado con las estructuras anidadas. Por ejemplo:

struct A {
   int i;
   int j;
   int k;
};

struct B {
   int x;
   struct C {
      char c;
      char d;
   } y;
   int z;
};

A ejemploA = {10, 20, 30};
B ejemploB = {10, {'a', 'b'}, 20};

Cada nueva estructura anidada deberá inicializarse usando la pareja correspondiente de llaves "{}", tantas veces como sea necesario.

Asignación de estructuras:  

La asignación de estructuras está permitida, pero sólo entre variables del mismo tipo de estructura, salvo que se usen constructores, y funciona como la intuición dice que debe hacerlo.

Veamos un ejemplo:

struct Punto { 
   int x, y; 
   Punto() {x = 0; y = 0;} 
} Punto1, Punto2;
 
int main() { 
   Punto1.x = 10; 
   Punto1.y = 12; 
   Punto2 = Punto1; 
}

La línea:

Punto2 = Punto1; 

equivale a:

Punto2.x = Punto1.x; 
Punto2.y = Punto1.y; 

Arrays de estructuras:   

La combinación de las estructuras con los arrays proporciona una potente herramienta para el almacenamiento y manipulación de datos.

Ejemplo:

struct Persona { 
   char Nombre[65]; 
   char Direccion[65]; 
   int AnyoNacimiento; 
} Plantilla[200]; 

Vemos en este ejemplo lo fácil que podemos declarar el array Plantilla que contiene los datos relativos a doscientas personas.

Podemos acceder a los datos de cada uno de ellos:

cout << Plantilla[43].Direccion; 

O asignar los datos de un elemento de la plantilla a otro:

Plantilla[0] = Plantilla[99]; 

Estructuras anidadas:   

También está permitido anidar estructuras, con lo cual se pueden conseguir superestructuras muy elaboradas.

Ejemplo:

struct stDireccion { 
   char Calle[64]; 
   int Portal; 
   int Piso; 
   char Puerta[3]; 
   char CodigoPostal[6]; 
   char Poblacion[32]; 
};
 
struct stPersona { 
   struct stNombre { 
      char Nombre[32]; 
      char Apellidos[64]; 
   } NombreCompleto; 
   stDireccion Direccion; 
   char Telefono[10]; 
};
... 

En general, no es una práctica corriente definir estructuras dentro de estructuras, ya que resultan tener un ámbito local, y para acceder a ellas se necesita hacer referencia a la estructura más externa.

Por ejemplo para declarar una variable del tipo stNombre hay que utilizar el operador de acceso (::):

stPersona::stNombre NombreAuxiliar; 

Sin embargo para declarar una variable de tipo stDireccion basta con declararla:

stDireccion DireccionAuxiliar;
  > >>
Free Web Hosting