struct vs class en C++: la única diferencia real
Respuesta corta: son la misma cosa con un valor por defecto invertido: los miembros de un struct son públicos por defecto; los de una class, privados. Lo mismo pasa con la herencia. Todo lo demás — métodos, constructores, herencia, plantillas — funciona igual en ambos.
Aviso: nuestro motor en el navegador cubre el núcleo didáctico y todavía no soporta struct/class definidos por el usuario, así que estos fragmentos son para tu compilador de escritorio. Los conceptos son material central de entrevista, así que sigue leyendo.
Prueba: una palabra clave cambia el acceso
struct PointS {
int x; // público (valor por defecto de struct)
int y;
};
class PointC {
int x; // privado (valor por defecto de class)
int y;
};
int main() {
PointS s;
s.x = 1; // vale — x es público
PointC c;
c.x = 1; // ERROR: 'int PointC::x' es privado
}
Esa es toda la diferencia mecánica. Cambia class por struct en el ejemplo y el error desaparece. Nada más distingue a las dos palabras clave.
Ambos pueden hacer de todo
// Un struct con métodos, constructor y miembros privados: perfectamente legal
struct CuentaBancaria {
private:
double saldo = 0;
public:
explicit CuentaBancaria(double inicial) : saldo(inicial) {}
void ingresar(double cantidad) {
if (cantidad > 0) saldo += cantidad;
}
double getSaldo() const { return saldo; }
};
// Una class usada como simple paquete de datos: también legal, solo que raro
class Config {
public:
int ancho;
int alto;
};
Entonces, si son intercambiables, ¿por qué existen las dos? Historia y convención. C++ heredó struct de C (donde solo puede contener datos) y añadió class para la programación orientada a objetos — y después las convirtió en la misma construcción con valores por defecto distintos.
La convención que siguen los proyectos reales
struct= paquete de datos pasivo. Si todo el asunto es «estos valores viajan juntos» y cualquier combinación es válida en cualquier momento — un punto 2D, un color RGB, un registro de configuración — usastructcon miembros públicos.class= cosas con reglas. Si ciertas combinaciones de valores son inválidas, o las operaciones deben mantener invariantes («el saldo nunca queda negativo»), usaclass: datos privados y métodos que hacen cumplir las reglas.
La prueba del olor: ¿alguna asignación arbitraria de sus miembros públicos produciría un objeto sin sentido? Sí → class. No → struct.
El segundo valor por defecto que todos olvidan: la herencia
struct DerivadaS : Base { }; // hereda de forma PUBLICA por defecto
class DerivadaC : Base { }; // hereda de forma PRIVADA por defecto
Misma palabra clave, mismo valor por defecto invertido — pero para la herencia en vez de para los miembros. En la práctica conviene escribir siempre el nivel de acceso de forma explícita al heredar, pero a los entrevistadores les encanta este detalle.
Preguntas de entrevista sobre este tema
- «¿Qué diferencia hay entre struct y class en C++?» — el acceso por defecto a los miembros y el tipo de herencia por defecto; nada más. (En C la respuesta es otra historia: un struct no puede tener métodos.)
- «¿Es struct más rápido que class?» — no. Código idéntico, rendimiento idéntico; solo cambia el acceso por defecto.
- «¿Cuándo elegirías struct a propósito?» — agregados pasivos (puntos, colores, DTO) y contextos de interoperabilidad donde importa un diseño compatible con C.
- «¿Qué es un agregado?» — a grandes rasgos: un struct sin constructores de usuario ni miembros privados, inicializable con llaves:
Point p {1, 2};. Una clase con invariantes no puede ser agregado, y eso es deliberado. - «¿Para qué hacer privados los miembros?» — para que el objeto pueda garantizar sus propias reglas (invariantes); con datos públicos cualquiera puede romperlas y nunca podrás añadir validación sin romper a todos los que llaman.
Practica en tu compilador de escritorio
- Escribe la prueba
PointS/PointCde arriba y lee el error exacto del compilador parac.x = 1. - Cambia
PointCastructy confirma que el error desaparece — mismo código, una palabra. - Escribe
class Temperaturacon uncelsiusprivado y unset(double)que rechace valores por debajo de −273,15 — eso es una invariante, justo lo que un struct con datos públicos no puede proteger.
Relacionado: pasar por valor, puntero o referencia (cómo viajan estos objetos a las funciones), stack vs heap (dónde viven), 15 preguntas de entrevista para juniors (esta es la 14).