static const Membro Valore vs. Membro enum: Quale metodo è migliore & amp; Perché?

StackOverflow https://stackoverflow.com/questions/204983

  •  03-07-2019
  •  | 
  •  

Domanda

Se vuoi associare un valore costante a una classe, qui ci sono due modi per raggiungere lo stesso obiettivo:

class Foo
{
public:
    static const size_t Life = 42;
};

class Bar
{
public:
    enum {Life = 42};
};

Sintatticamente e semanticamente sembrano essere identici dal punto di vista del cliente:

size_t fooLife = Foo::Life;
size_t barLife = Bar::Life;

C'è qualche motivo diverso dal solo puro stile per cui uno sarebbe preferibile ad un altro?

È stato utile?

Soluzione

L'hack enum era necessario perché molti compilatori non supportavano l'inizializzazione sul posto del valore. Poiché questo non è più un problema, scegli l'altra opzione. I compilatori moderni sono anche in grado di ottimizzare questa costante in modo che non sia necessario spazio di archiviazione per essa.

L'unico motivo per non usare la variante static const è se vuoi vietare prendendo l'indirizzo del valore: non puoi prendere l'indirizzo di un < code> enum mentre puoi prendere l'indirizzo di una costante (e questo richiederebbe al compilatore di riservare spazio per il valore dopo tutto, ma solo se il suo indirizzo è veramente preso).

Inoltre, l'assunzione dell'indirizzo produrrà un errore del tempo di collegamento a meno che anche la costante sia esplicitamente definita . Si noti che può ancora essere inizializzato nel sito della dichiarazione:

struct foo {
    static int const bar = 42; // Declaration, initialization.
};

int const foo::bar; // Definition.

Altri suggerimenti

Non sono identici:

size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;

Una differenza è che l'enum definisce un tipo che può essere usato come parametro del metodo, ad esempio per ottenere una migliore verifica del tipo. Entrambi sono trattati come costanti di tempo di compilazione dal compilatore, quindi dovrebbero generare codice identico.

I valori

?? const statici sono trattati come valori r proprio come enum nel 99% del codice che vedrai. I valori r costanti non hanno mai memoria generata per loro. Il vantaggio delle costanti enum è che non possono diventare valori l in quell'altro 1%. I valori static const sono di tipo sicuro e consentono float, stringhe c, ecc.

Il compilatore renderà Foo :: Life un valore l se ha memoria associata. Il solito modo per farlo è quello di prendere il suo indirizzo. per esempio. & amp; Foo :: Vita;

Ecco un esempio sottile in cui GCC utilizzerà l'indirizzo:

int foo = rand()? Foo::Life: Foo::Everthing;

Il codice generato dal compilatore utilizza gli indirizzi di Life e Everything . Peggio ancora, questo produce solo un errore del linker sugli indirizzi mancanti per Foo :: Life e Foo :: Everything . Questo comportamento è completamente conforme allo standard, sebbene ovviamente indesiderabile. Esistono altri modi specifici del compilatore che ciò può accadere e tutti gli standard conformi.

Una volta che hai un compilatore c ++ 11 conforme, il codice corretto sarà

class Foo {
 public:
  constexpr size_t Life = 42;
};

Questo è garantito per essere sempre un valore l ed è sicuro per il tipo, il migliore dei due mondi.

Bene, se necessario, puoi prendere l'indirizzo di un valore membro const statico. Devi dichiarare una variabile membro separata di tipo enum per prenderne l'indirizzo.

Un'altra terza soluzione?

Una sottile differenza è che l'enum deve essere definito nell'intestazione e visibile a tutti. Quando si evitano le dipendenze, questo è un dolore. Ad esempio, in un PImpl, l'aggiunta di un enum è in qualche modo controproducente:

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      enum { Life = 42 } ;
   private :
      MyImpl * myImpl ;
}

Un'altra terza soluzione sarebbe una variazione di " const static " alternativa proposta nella domanda: dichiarazione della variabile nell'intestazione, ma definizione nella fonte:

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      static const int Life ;
   private :
      MyImpl * myImpl ;
}

.

// MyPImpl.cpp
const int MyPImpl::Life = 42 ;

Nota che il valore di MyPImpl :: Life è nascosto all'utente di MyPImpl (che include MyPImpl.hpp).

Ciò consentirà all'autore MyPimpl di modificare il valore di " Life " secondo necessità, senza la necessità che l'utente MyPImpl si ricompili, così come l'obiettivo generale di PImpl.

Vale la pena conoscere l'hack enum per diversi motivi. Innanzitutto, l'hack enum si comporta in qualche modo più come un #define che una const, e talvolta è quello che vuoi. Ad esempio, è legale prendere l'indirizzo di una const, ma non è legale prendere l'indirizzo di una enum, e in genere non è nemmeno legale prendere l'indirizzo di un #define. Se non vuoi lasciare che le persone ottengano un puntatore o un riferimento a una delle tue costanti integrali, un enum è un buon modo per applicare quel vincolo. (Per ulteriori informazioni sull'applicazione dei vincoli di progettazione mediante decisioni di codifica, consultare l'articolo 18.) Inoltre, sebbene i compilatori validi non riservino spazio per oggetti const di tipo integrale (a meno che non si crei un puntatore o un riferimento all'oggetto), i compilatori sciatti possono, e potresti non essere disposto a mettere da parte la memoria per tali oggetti. Come #defines, gli enum non portano mai a quel tipo di allocazione di memoria non necessaria.

Libro c ++ efficace

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top