¿Deberíamos agregar constructores a estructuras?

14

A menudo usamos estructuras c ++ para definir la estructura de datos en lugar de la clase, que puede ser un módulo completo con métodos miembros. Ahora en el fondo, sabemos que ambos son iguales (hablando en términos generales).

El hecho de que a menudo usemos / tratemos estructuras como entidades de solo datos crea esta necesidad de no agregar también constructores predeterminados. Pero los constructores siempre son excelentes, simplifican las cosas y ayudan a eliminar errores.

¿Sería mal visto si agrego constructores predeterminados a mis estructuras de datos?

¿La implementación del constructor predeterminado también hace que la estructura no sea POD (tipo de datos antiguo simple) siempre que se cumplan otros criterios?

Para poner las cosas en perspectiva, considere un ejemplo simple, pero en realidad la estructura sería mucho más grande.

struct method
{
    char    name[32];
    float   temperature;
    int     duration;
};

Cada vez que creo un método, tengo que preocuparme (por decir lo menos) si olvidé establecer algún valor. Imagine que me olvido de configurar temperaturey aplicar el método al sistema que ahora es un valor alto aleatorio y causa caos. O me olvidé de configurar durationy ahora el método se aplica por una alta duración desconocida.

¿Por qué debería asumir la responsabilidad de inicializar el objeto cada vez en lugar de implementar su constructor que lo garantiza?

zadane
fuente
Si necesita hacer cumplir que solo se permiten ciertos valores, entonces no tiene un tipo de datos antiguo simple. Si solo desea formas convenientes de inicializar estructuras, las funciones antiguas simples lo harán.
Doval
Depende de lo que estén haciendo estos constructores. Creo que es completamente razonable tener un constructor en una estructura simple si solo establece valores de campo de manera básica.
Gort the Robot
@Doval esa no es la pregunta, actualicé la publicación. Steven: sí, los constructores solo asignarán valores predeterminados.
zadane
@StevenBurnap: si el constructor hace algo más que establecer valores de campo de manera básica, es más apropiado tenerlo. Incluso en una estructura.
Jan Hudec
2
Lo que quiero decir es que si comienzas a encontrar una lógica complicada en el constructor, es probable que debas convertirla en una clase. (En mi humilde opinión) Pero en realidad es solo una pregunta de estilo como la única diferencia real entre structy classes que uno predeterminado es privado y el otro público.
Gort the Robot

Respuestas:

13

A veces es apropiado agregar un constructor a una estructura y otras no.

Agregar constructor (cualquier constructor) a una estructura evita el uso de inicializador agregado en ella. Entonces, si agrega un constructor predeterminado, también tendrá que definir un constructor no predeterminado inicializando los valores. Pero si desea asegurarse de que siempre inicializa a todos los miembros, es apropiado.

Agregar constructor (cualquier constructor, nuevamente) hace que no sea POD, pero en C ++ 11 la mayoría de las reglas que anteriormente se aplicaban a POD solo se cambiaron para aplicarse a objetos de diseño estándar y agregar constructores no lo rompen. Entonces, el inicializador agregado es básicamente lo único que se pierde. Pero a menudo también es una gran pérdida.

Jan Hudec
fuente
8

Con C ++ 11 puedes hacer

struct method
{
    char    name[32] {};
    float   temperature = 42.141521;
    int     duration = -6;
};

Y cada vez que olvida inicializar algo, obtiene la inicialización predeterminada.

Vorac
fuente
-1

Respuesta rápida:

Depende de lo que quieras lograr.

Respuesta larga, extendida y aburrida:

Golpeaste el clavo.

Por lo general, no me gusta que "C ++" permita que "Estructura (s)" permita declarar métodos. Preferiblemente, utilizo "Clase (s)" explícita para los métodos requeridos y "Estructura (s)" de POD solo para los campos.

Sin embargo, estoy de acuerdo en que algunas operaciones simples básicas, como:

  • asignar valores iniciales ("constructor")
  • hacer una copia de una estructura ("constructor de copias")
  • asignar valores a una estructura existente ("operador de asignación de sobrecarga")

Son necesarios y, en esas circunstancias, los métodos para las estructuras tienen sentido.

Sugerencia

Otra posible solución es usar estructuras POD, pero aún así tratarlas conceptualmente como clases y objetos.

Envuelva esas declaraciones en un espacio de nombres y agregue funciones globales para las acciones más importantes.

La declaración del código podría ser similar a esto:

namespace Customers
{
  struct CustomerStruct
  {
    char[255] FirstName;
    char[255] LastName;
    int Age;
    bool IsAlive;
    bool IsMarried;
  }; // struct

  CustomerStruct* CreateCustomer
  (
    char* NewFirstName;
    char* NewLastName;
    int NewAge;
    bool NewIsAlive;
    bool NewIsMarried;
  )
  {
    CustomerStruct* NewCustomer = new CustomerStruct();
      NewCustomer->FirstName = NewFirstName;
      NewCustomer->LastName = NewLastName;
      NewCustomer->Age = NewAge;
      NewCustomer->IsAlive = NewIsAlive;
      NewCustomer->IsMarried = NewIsMarried;
    return NewCustomer;
  } // CustomerStruct* CreateCustomer (...)

} // namespace

El código que aplica la solución podría ser algo como esto:

#include <Customers>

using Customers;

int main (...)
{
   int ErrorCode = 0;

   CustomerClass* ThisCustomer =
     Customers::CreateCustomer
      ("John", "Doe", 23, true, true);

   // do something with "ThisCustomer"

   delete ThisCustomer;

   return ErrorCode;
} // int main(...)

Este enfoque alternativo es mejor cuando se requiere una gran asignación de memoria de datos o al interactuar con otras bibliotecas compartidas de bajo nivel.

Este enfoque, con algunos cambios, se aplica en Game Development.

Extra

Personalmente, considero una extensión de sintaxis para "C ++", o incluso, un nuevo PL basado en "C ++" que resuelve este problema:

// "Plain Old Data" Structure
// No Methods, No "Functors", allowed
strict struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;
}; // strict struct

// Object Oriented "Plain Old Data" Structure
// Yes, Methods and "Functors" allowed
relaxed struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;

  public void Foo();
  public void Bar();

  public (void*) (SomeFunctor) ();
}; // relaxed struct

// Class and Object Oriented
class CustomerClass
{
  public char[255] FirstName;
  public char[255] LastName;
  public int Age;
  public bool IsAlive;
  public bool IsMarried;

  public void Foo();
  public void Bar();
}; // class

Salud.

umlcat
fuente