Por qué agrupar datos
Hasta ahora cada variable iba por su cuenta: un int aquí, un string allá. Pero los programas reales tratan con cosas formadas por varias piezas: un punto tiene una x y una y, un estudiante tiene un nombre, una edad y un promedio. Pasar todo eso como variables sueltas y separadas es propenso a errores; nada las mantiene unidas, y una función que necesite las tres debe recibir los tres parámetros.
Un struct soluciona esto. Define un nuevo tipo que agrupa variables relacionadas —sus miembros— en una sola unidad. Una vez definido, tratas todo el conjunto como un único valor que puedes almacenar, copiar y pasar a funciones.
Accedes a cada miembro con el operador punto (s.name, s.age). El punto y coma tras la } de cierre de la definición de un struct es obligatorio; omitirlo es uno de los errores de compilación más habituales entre principiantes en C++.
Inicializar un struct
Asignar cada campo a mano funciona, pero es verboso y es fácil olvidar un miembro. La opción más limpia es la inicialización por agregados: enumera los valores entre llaves, en el mismo orden en que se declararon los miembros.
Cuidado con el caso por defecto vacío: Point p; (sin llaves) deja x e y con valores basura, porque los miembros de tipos integrados no se ponen a cero automáticamente. Point p{}; los inicializa por valor a 0. Prefiere las llaves. También puedes fijar valores por defecto directamente en la definición, de modo que incluso Point p; empiece limpio:
Structs como parámetros de funciones
Un struct es un único valor, así que una función puede recibir un solo parámetro en lugar de tres. Solo recuerda que pasar un struct por valor copia todos sus miembros. Para cualquier cosa más grande que un par de ints, pásalo por referencia const para evitar la copia: la misma regla que viste en la página de referencias.
Devolver un struct completo desde una función es la manera idiomática de retornar varios valores a la vez, mucho más limpia que manejar varias referencias de salida.
Añadir funciones miembro y constructores
Un struct no se limita a datos. Puede contener funciones miembro que operan sobre sus propios miembros, y un constructor que inicializa el objeto en el momento en que se crea. Aquí es donde un struct empieza a parecerse a un pequeño objeto con comportamiento.
Dentro de una función miembro se accede a los miembros por su nombre (width, height): se refieren a la copia de este objeto. Marcar area() const le indica al compilador que la función solo lee el objeto, lo que te permite llamarla también sobre valores const Rectangle. Los constructores son un tema en sí mismos, incluida esa sintaxis de lista de inicialización : width(w); la página de constructores lo trata en profundidad.
Struct vs. class: la diferencia real
Puede que hayas oído que "los structs son para datos y las clases para objetos". Eso es una convención, no una regla del lenguaje. En C++ un struct y una class son casi lo mismo: la única diferencia integrada es el nivel de acceso por defecto.
struct S {
int x; // public por defecto
};
class C {
int x; // private por defecto
};
Los miembros de un struct son public salvo que los marques de otra forma; los de una class empiezan siendo private. Eso es todo: ambos pueden tener constructores, funciones miembro, herencia y todo lo demás. Incluso puedes añadir especificadores de acceso explícitos a un struct para ocultar miembros:
La conclusión práctica: usa struct para agrupaciones transparentes de datos donde cada campo está pensado para tocarse libremente (un Point, un Color, un registro de configuración), y recurre a class cuando quieras proteger el estado interno tras una interfaz pública. El compilador los trata igual; la palabra clave solo expresa tu intención.
Arreglos y vectores de structs
Como un struct es un tipo normal y corriente, puedes meter muchos de ellos en un arreglo o un vector y recorrerlos como cualquier otro valor.
Fíjate en las llaves anidadas: cada {"Keyboard", 49.99} inicializa por agregados un Product, y las llaves exteriores construyen el vector. Usar const Product& en el bucle basado en rangos evita copiar cada struct en cada iteración; si quitas el & duplicarías cada elemento innecesariamente.
Siguiente: Enums
Los structs te permiten construir un tipo a partir de varios valores agrupados. El próximo capítulo va en sentido contrario: un enum define un tipo que puede contener exactamente un valor de un conjunto pequeño y con nombre, perfecto para cosas como Color::Red, Direction::North o el estado de una máquina de estados. Verás cómo enum class te da constantes legibles y con seguridad de tipos que combinan de forma natural con los structs y clases que ahora estás construyendo.
Preguntas frecuentes
¿Qué es un struct en C++?
Un struct es un tipo definido por el usuario que agrupa varias variables relacionadas (llamadas miembros) bajo un mismo nombre. En lugar de manejar por separado las variables string name, int age y double gpa, las agrupas en un único tipo Student y pasas ese objeto de un lado a otro. Un struct también puede tener funciones miembro y constructores: en C++ moderno es una clase completa cuyos miembros son public por defecto.
¿Cuál es la diferencia entre struct y class en C++?
Técnicamente solo el nivel de acceso por defecto: los miembros de un struct son public salvo que indiques lo contrario, mientras que los de una class son private por defecto. Lo mismo ocurre con la herencia. Todo lo demás (constructores, funciones miembro, métodos, herencia) funciona igual. Por convención, los programadores usan struct para agrupaciones de datos simples y class para tipos con invariantes e internos ocultos.
¿Cómo se inicializa un struct en C++?
La forma más sencilla es la inicialización por agregados con llaves en el orden de los miembros: Point p{3, 4};. También puedes asignar cada campo a mano con el operador punto (p.x = 3;), dar valores por defecto a los miembros en la definición (int x = 0;), o escribir un constructor para que el struct se configure solo. La inicialización con llaves es la preferida porque no deja campos sin inicializar de forma silenciosa.