resueltos - Estructuras de C++ con funciones miembro vs. clases con variables públicas
programacion orientada a objetos ejemplos c++ (6)
Esta es realmente una cuestión de buena forma / mejores prácticas. Utilizo las estructuras en C ++ para formar objetos que están diseñados para almacenar datos básicamente, en lugar de crear una clase con una tonelada de métodos de acceso que no hacen nada más que obtener / establecer los valores. Por ejemplo:
struct Person {
std::string name;
DateObject dob;
(...)
};
Si imaginas 20 variables más allí, escribir esto como una clase con miembros privados y personas con accesos de 40 y tantos es un dolor para administrar y me parece un desperdicio.
A veces, sin embargo, es posible que deba agregar también algún tipo de funcionalidad mínima a los datos. En el ejemplo, digamos que a veces también necesito la edad, basada en dob:
struct Person {
std::string name;
DateObject dob;
(...)
int age() {return calculated age from dob;}
}
Por supuesto, para cualquier funcionalidad compleja haría una clase, pero solo para una funcionalidad simple como esta, ¿es este "mal diseño"? Si utilizo una clase, ¿es una mala forma mantener las variables de datos como miembros de la clase pública, o simplemente necesito aceptarlo y hacer clases con un montón de métodos de acceso? Entiendo las diferencias entre clases y estructuras, solo pregunto sobre las mejores prácticas.
"Use una estructura solo para objetos pasivos que transportan datos; todo lo demás es una clase".
di google guidlines , lo hago de esta manera y me parece una buena regla. Además, creo que puedes definir tu propia pragmática o desviarte de esta regla si realmente tiene sentido.
Creo que hay dos principios de diseño importantes a considerar aquí:
Oculte la representación de una clase a través de una interfaz si hay alguna invariante en esa clase.
Una clase tiene un invariante cuando existe un estado no válido para esa clase. La clase debe mantener su invariante en todo momento.
Considere un tipo de
Point
que represente un punto geométrico 2D. Esto solo debería ser unastruct
con miembros públicos de datosx
ey
. No hay tal cosa como un punto inválido. Cada combinación de valoresx
ey
está perfectamente bien.En el caso de una
Person
, si tiene invariantes depende completamente del problema en cuestión. ¿Consideras cosas como un nombre vacío como un nombre válido? ¿Puede laPerson
tener alguna fecha de nacimiento? Para su caso, creo que la respuesta es sí y su clase debe mantener a los miembros públicos.Las funciones que no son miembros que no son amigos mejoran la encapsulación.
No hay ninguna razón por la que su función de
age
deba implementarse como una función miembro. El resultado de laage
se puede calcular utilizando la interfaz pública dePerson
, por lo que no tiene ninguna razón para ser una función miembro. Colóquelo en el mismo espacio de nombres quePerson
para que se encuentre por búsqueda dependiente del argumento. Las funciones encontradas por ADL son parte de la interfaz de esa clase; simplemente no tienen acceso a datos privados.Si lo hiciera una función miembro y un día introdujera algún estado privado a
Person
, tendría una dependencia innecesaria. De repente, laage
tiene más acceso a los datos de lo que necesita.Ver: Cómo las funciones que no son miembros mejoran la encapsulación
Así que aquí está cómo lo implementaría:
struct Person {
std::string name;
DateObject dob;
};
int age(const Person& person) {
return calculated age from person.dob;
}
En C ++, la única diferencia entre las estructuras y las clases es que las estructuras son públicamente visibles de forma predeterminada. Una buena guía es usar las estructuras como datos antiguos (POD) que solo contienen datos y usan clases para cuando se requiere más funcionalidad (funciones miembro).
Es posible que aún se esté preguntando si solo debe tener variables públicas en la clase o usar funciones miembro; Consideremos el siguiente escenario.
Digamos que tienes una clase A
que tiene una función GetSomeVariable
que es simplemente un captador para una variable privada:
class A
{
double _someVariable;
public:
double GetSomeVariable() { return _someVariable; }
};
¿Qué pasa si, veinte años después, el significado de esa variable cambia, y usted tiene que, digamos, multiplicarlo por 0.5? Cuando se utiliza un captador, es simple; solo devuelve la variable multiplicada por 0.5:
double GetSomeVariable() { return 0.5*_someVariable; }
Al hacer esto, permite un fácil mantenimiento y una fácil modificación.
En C ++, los Structs son clases, con la única diferencia (en la que puedo pensar, al menos) es que en Structs los miembros son públicos por defecto, pero en las clases son privados. Esto significa que es perfectamente aceptable usar Structs como usted lo hace, este artículo lo explica bien.
No quiero encender una guerra santa aquí; Normalmente lo diferencio de esta manera:
- Para los objetos POD (es decir, solo datos, sin comportamiento expuesto) declare los elementos internos públicos y acceda a ellos directamente. El uso de la palabra clave
struct
es conveniente aquí y también sirve como una sugerencia del uso del objeto. - Para los objetos que no son POD, declare los elementos internos como privados y defina los captadores / setters públicos. El uso de
class
palabra clave declass
es más natural en estos casos.
Si desea un titular de datos, prefiera la estructura sin ningún método de obtener / establecer.
Si hay más, como en este caso "Persona".
- Modela la entidad del mundo real,
- Tiene estado y comportamiento definidos,
- Interactúa con el mundo externo,
- Exhibe relación simple / compleja con otras entidades,
- puede evolucionar horas extras,
entonces es un candidato perfecto para una clase.