C++
Structs: agrupar datos relacionados
Cómo agrupar datos de tipos distintos bajo un solo nombre, acceder a sus miembros, y la relación de un struct con una clase.
De dónde venimos
Arrays resolvió cómo agrupar varios datos del mismo tipo bajo un nombre. Pero un dato del mundo real casi nunca es tan uniforme: una persona tiene un nombre (std::string), una edad (int) y una altura (double) — tres tipos distintos que, hasta ahora, tendrías que manejar como tres variables sueltas sin ninguna relación explícita entre ellas. Esta página cierra ese hueco.
También retoma punteros: un struct se puede apuntar igual que cualquier otra variable, y eso introduce un operador nuevo que vas a ver todo el tiempo en código real.
Qué es un struct
Un struct (structure) es un tipo de dato definido por ti que agrupa varias variables — llamadas miembros o campos — bajo un solo nombre.
#include <iostream>
#include <string>
struct Persona {
std::string nombre;
int edad;
double altura;
};
int main() {
Persona p;
p.nombre = "Camilo";
p.edad = 30;
p.altura = 1.75;
std::cout << p.nombre << " tiene " << p.edad << " años y mide " << p.altura << "m\n";
return 0;
}g++ -std=c++17 -Wall -o persona persona.cpp
./personaCamilo tiene 30 años y mide 1.75mDos cosas nuevas en este ejemplo:
struct Persona { ... };define un tipo nuevo llamadoPersona. No crea ninguna variable todavía, solo describe la forma que va a tener.Persona p;crea una variable de ese tipo, igual queint x;crea una variable de tipoint. A partir de ahí,ptiene sus propiosnombre,edadyaltura.
No olvides el ; después de la llave que cierra el struct (};). Es una de las pocas construcciones de C++ donde el punto y coma de cierre es obligatorio y fácil de olvidar — el compilador se queja de formas confusas cuando falta.
El operador punto (.)
p.nombre se lee “el miembro nombre de p”. El operador punto accede a un campo de una variable struct. Puedes leerlo y escribirlo como cualquier variable normal, porque eso es exactamente lo que es:
#include <iostream>
struct Punto {
double x;
double y;
};
int main() {
Punto origen{0.0, 0.0}; // inicialización agregada, en el mismo orden que los miembros
Punto p{3.0, 4.0};
p.x = p.x + 1.0; // leer y escribir un miembro
std::cout << "origen: (" << origen.x << ", " << origen.y << ")\n";
std::cout << "p: (" << p.x << ", " << p.y << ")\n";
return 0;
}origen: (0, 0)
p: (4, 4)
Punto p{3.0, 4.0}; es preferible a asignar campo por campo como en el primer ejemplo: es más corto, y si te falta un valor el compilador lo inicializa en cero en vez de dejarlo con basura — la misma advertencia sobre variables sin inicializar que ya viste, pero resuelta de fábrica.
Structs como parámetros de función
Un struct se pasa a una función exactamente con las mismas reglas que viste en Funciones: por valor copia todos sus miembros, por referencia no.
#include <iostream>
#include <string>
struct Persona {
std::string nombre;
int edad;
};
void cumplirAnios(Persona persona) { // por VALOR: copia completa
persona.edad = persona.edad + 1; // solo cambia la copia
}
void cumplirAniosDeVerdad(Persona& persona) { // por REFERENCIA: sin copia
persona.edad = persona.edad + 1; // cambia el original
}
int main() {
Persona p{"Ana", 29};
cumplirAnios(p);
std::cout << p.edad << "\n"; // 29, no cambió
cumplirAniosDeVerdad(p);
std::cout << p.edad << "\n"; // 30, sí cambió
return 0;
}29
30
Un Persona es pequeño (un string y un int), pero copiar un struct con muchos campos, o con un std::vector grande adentro, cuesta tiempo real. La regla práctica de Funciones aplica igual aquí: pasa structs grandes por const& cuando la función solo necesita leerlos, y por & (sin const) cuando necesita modificarlos.
Punteros a struct: el operador flecha (->)
Como cualquier variable, un struct se puede apuntar con &, siguiendo Punteros. Acceder a un miembro a través de un puntero es tan común que C++ tiene un operador dedicado para eso: la flecha ->.
#include <iostream>
#include <string>
struct Persona {
std::string nombre;
int edad;
};
int main() {
Persona p{"Ana", 29};
Persona* ptr = &p; // ptr apunta a p
std::cout << (*ptr).nombre << "\n"; // funciona, pero es incómodo de leer
std::cout << ptr->nombre << "\n"; // lo mismo, forma habitual
ptr->edad = 30; // modifica p.edad a través del puntero
std::cout << p.edad << "\n";
return 0;
}Ana
Ana
30ptr->nombre es exactamente (*ptr).nombre: primero desreferencia el puntero para llegar al struct, después accede al miembro. La flecha simplemente evita escribir los paréntesis y el * por separado. Regla rápida para elegir el operador:
| Tienes… | Usas |
|---|---|
| La variable struct directamente | . (p.nombre) |
| Un puntero al struct | -> (ptr->nombre) |
En qué se diferencia de una clase
Ya usaste tipos que se comportan de forma parecida a un struct pero con reglas extra: std::string, std::vector. Esos son clases, no structs. La diferencia técnica en C++ es mínima y se resume en una frase: en un struct los miembros son públicos por defecto; en una clase son privados por defecto. Eso es todo — un struct y una clase son, mecánicamente, la misma cosa con un valor por defecto distinto.
Por convención (no por obligación del compilador), en C++ se usa struct para agrupar datos simples sin comportamiento propio — a este patrón se le llama plain old data (“datos simples”, sin lógica ni validación propia) — y class cuando el tipo también controla su propio comportamiento y protege sus datos de que se modifiquen desde afuera sin control. Persona y Punto de esta página son buenos structs; std::string, que valida internamente sus propios datos y ofrece métodos como .size(), es una buena clase.
No confundas “público por defecto” con “siempre debe ser público”. Puedes escribir private: dentro de un struct y public: dentro de una class — el compilador lo acepta igual. La diferencia de default es la única regla real; el resto es convención de estilo que la comunidad de C++ sigue casi universalmente.
Lo que deliberadamente no vimos aquí
Todo lo de esta página son datos agrupados sin comportamiento propio: ningún struct de aquí sabe hacer nada por sí mismo, solo guarda valores. Definir funciones propias dentro del tipo (métodos), controlar qué miembros son visibles desde afuera, y constructores que inicializan automáticamente — eso es programación orientada a objetos, y es el tema de la página siguiente.
Siguiente paso
Con variables, control de flujo, funciones, arrays, punteros y structs ya puedes leer y escribir la mayoría del C++ “de todos los días”. Falta la otra mitad de lo que hace std::string o std::vector distintos de un struct: comportamiento propio y datos protegidos. Sigue con Clases.