Dovremmo aggiungere costruttori alle strutture?

13

Spesso usiamo le strutture c ++ per definire la struttura dei dati rispetto alla classe, che può essere un modulo completo con i metodi dei membri. Ora in fondo, sappiamo che entrambi sono uguali (vagamente parlando).

Il fatto che spesso usiamo / trattiamo le strutture come entità solo dati crea questo bisogno che non aggiungiamo anche costruttori predefiniti. Ma i costruttori sono sempre grandi, semplificano le cose e aiutano a eliminare gli errori.

Sarebbe disapprovante se aggiungessi costruttori predefiniti alle mie strutture dati?

L'implementazione del costruttore default rende anche struct Non-POD (plain old data type) a condizione che vengano soddisfatti altri criteri?

Per mettere le cose in prospettiva, considera un semplice esempio, ma in realtà la struttura sarebbe molto più grande.

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

Ogni volta che creo un metodo, devo preoccuparmi (per non dire altro) se mi sono dimenticato di impostare un valore. Immagina di dimenticare di impostare temperature e applicare il metodo al sistema che ora è un valore elevato casuale e causa caos. O mi sono dimenticato di impostare duration e ora il metodo si applica per una durata elevata sconosciuta.

Perché dovrei assumermi la responsabilità di inizializzare l'oggetto ogni volta invece di implementare il suo costruttore che lo garantisce?

    
posta zadane 11.11.2014 - 17:51
fonte

3 risposte

13

A volte è opportuno aggiungere il costruttore a una struct e talvolta non lo è.

L'aggiunta di un costruttore (qualsiasi costruttore) a una struttura impedisce l'utilizzo di inizializzatore aggregato su di essa. Quindi, se aggiungi un costruttore predefinito, dovrai anche definire il costruttore non predefinito inizializzando i valori. Ma se vuoi assicurarti di inizializzare sempre tutti i membri, è appropriato.

L'aggiunta di un costruttore (qualsiasi costruttore, ancora una volta) lo rende non POD, ma in C ++ 11 la maggior parte delle regole applicate in precedenza a POD solo sono state modificate per essere applicate agli oggetti di layout standard e l'aggiunta di costruttori non lo interrompe. Quindi l'inizializzatore aggregato è fondamentalmente l'unica cosa che si perde. Ma spesso è anche una grande perdita.

    
risposta data 11.11.2014 - 18:29
fonte
8

Con C ++ 11 puoi fare

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

E ogni volta che dimentichi di inizializzare qualcosa, ottieni l'inizializzazione predefinita.

    
risposta data 13.11.2014 - 17:08
fonte
-1

Risposta rapida:

Dipende da cosa vuoi raggiungere.

Risposta lunga, estesa, noiosa:

Hai colpito l'unghia.

Di solito non mi piace che "C ++" permetta a "Struct (s)" di dichiarare metodi. Preferibilmente, utilizzo "Classi" esplicite per i metodi richiesti e P.O.D. "Struct (s)" per soli campi.

Tuttavia, sono d'accordo che alcune semplici operazioni di base, come:

  • assegna i valori iniziali ("costruttore")
  • crea una copia di una struttura ("costruttore di copie)
  • assegna valori a una struttura esistente ("overload assign operator")

Sono richiesti e, in quelle circostanze, i metodi per le strutture hanno senso.

Suggerimento

Un'altra possibile soluzione è usare il P.O.D. strutture, ma trattale ancora concettualmente come classi e oggetti.

Avvolgi quelle dichiarazioni in uno spazio dei nomi e, aggiungi funzioni globali, per le azioni più importanti.

La dichiarazione del codice potrebbe essere simile a questa:

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

Il codice che applica la soluzione potrebbe essere qualcosa del tipo:

#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(...)

Questo approccio alternativo è migliore quando è richiesta un'enorme allocazione di memoria di dati o l'interazione con altre librerie condivise di basso livello.

Questo approccio, con alcune modifiche, è applicato in Game Development.

Extra

Personalmente, considero un'estensione di sintassi per "C ++", o anche, una nuova P.L basata su "C ++". questo risolve questo 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

Saluti.

    
risposta data 11.11.2014 - 19:45
fonte

Leggi altre domande sui tag