Supongamos que tenemos que guardar los datos de los alumnos de toda una escuela, para poder trabajar con ellos en el programa. Específicamente, por cada alumno tenemos:
¿Cómo podríamos hacer para almacenar todos estos datos, por cada alumno?
Con las herramientas que vimos hasta el momento, la manera más natural es usar vector
, para poder tener listas de datos. Concentrémonos por ejemplo en los nombres: Si solamente quisiéramos guardar los nombres de los alumnos, sería fácil, pues tendríamos un vector<string> nombres;
donde guardaremos todos los nombres y listo.
¿Qué pasa si queremos guardar tanto nombre como apellido? Podemos tener dos vectors diferentes:
vector<string> nombres; vector<string> apellidos;
Así, si por ejemplo hiciéramos nombres = {“Andrea”, “Pablo”, “Marta”}
y apellidos = {“Aluvia”, “Poncho”, “Muller”}
, tendríamos guardados los datos de 3 alumnos: Andrea Aluvia, Pablo Poncho y Marta Muller.
Notemos que de esta forma, tenemos que asegurarnos de guardar los apellidos y los nombres en el mismo orden, pues sino, no podemos recuperar qué nombre iba con qué apellido. Así, todos los vector deben estar coordinados de forma que el primer dato en todos corresponde al “alumno 1”, el segundo en todos corresponde al “alumno 2” y así siguiendo.
¿Y si tuviéramos que agregar los demás datos? Tendríamos que tener muchos vectors:
vector<string> nombres; vector<string> apellidos; vector<int> diasNacimiento; vector<int> mesesNacimiento; vector<int> annosNacimiento; vector<int> annosEscolaridad; vector<char> divisiones; vector<string> telefonos; vector<string> direcciones;
Si bien este mecanismo de mantener vectores paralelos funciona (y es usual utilizarlo por ejemplo en competencias de programación cuando hay pocos campos de información), en C++ existe una manera mejor de resolver esta situación, y son justamente los Struct, que estudiaremos en esta sección.
Un struct es en C++ un tipo de datos compuesto: Es decir, será un tipo de datos que se forma a partir de otros tipos más básicos.
Por ejemplo, una esquina en una ciudad viene dada generalmente indicando las dos calles que allí se cruzan. Podríamos tener así un tipo para las esquinas, que podríamos escribir así:
struct Esquina { string calle1; string calle2; };
o también de forma completamente equivalente:
struct Esquina { string calle1, calle2; };
Notar que las declaraciones de los struct terminan con un ;
, a diferencia de por ejemplo las funciones, que no llevan ese ;
.
De esta forma, acabamos de crear un nuevo tipo de datos, llamado Esquina
, y que se forma con dos componentes: calle1, de tipo string, y calle2, también de tipo string.
¿Cómo utilizamos este tipo? Podemos declarar variables de tipo Esquina exactamente igual que declaramos las de otros tipos como int o string:
Esquina e; Esquina e2; e.calle1 = "Cordoba"; e.calle2 = "Medrano"; e2 = e; // Ahora la variable e2 contiene la misma esquina que la variable e
El ejemplo muestra que podemos utilizar .
para acceder a los componentes individuales de un struct. Cada uno de estos componentes funciona como una variable independiente, del tipo correspondiente, y el struct lo que hace es funcionar como una sola “gran variable” que las une a todas. También en el ejemplo vemos que es válido utilizar el operador de asignación para copiar structs, igual que copiábamos variables de tipos básicos como int
. Esto lo que hará es copiar cada componente.
De manera similar a lo que ocurre con los vectors, es posible1) indicar un valor para todo el struct en lugar trabajar con componentes de a una, mediante el uso de llaves para dar los valores en el orden del struct. El ejemplo anterior por lo tanto se podría reescribir como:
Esquina e; Esquina e2; e = {"Cordoba","Medrano"}; e2 = e; // Ahora la variable e2 contiene la misma esquina que la variable e
O directamente en la declaracion:
Esquina e = {"Cordoba","Medrano"}; Esquina e2; e2 = e; // Ahora la variable e2 contiene la misma esquina que la variable e
Así, para representar la escuela anterior, podríamos tener un struct con la información del alumno:
struct Alumno { string nombre; string apellido; int diaNacimiento; int mesNacimiento; int annoNacimiento; int annoEscolaridad; char division; string telefono; string direccion; };
Y tener un solo vector, que guardará directamente Alumnos:
vector<Alumno> alumnos;
Como un struct define un nuevo tipo que puede usarse igual que los ya existentes, las funciones pueden recibir parámetros de tipo struct:
string nombreCompleto(Alumno a) { return a.nombre + " " + a.apellido; }