2013-06-12 9 views
20

Supponiamo di avere una classe che richiede alcune costanti per funzionare. Diverse funzioni membro richiedono l'uso di queste costanti. L'uso di #define è disapprovato poiché può causare collisioni. Le costanti sono modelli esadecimali di 8 o 16 bit e sono memorizzate come uint8_t o uint16_t. Queste costanti non cambiano anche da istanza ad istanza della classe, e quindi la memoria (anche se pochissima memoria) può essere salvata avendo una sola copia delle costanti.Uso variabile di C++ Static Const membro

C'è qualcosa di improprio, o forse di un modo migliore di realizzare quanto sopra, invece di limitarsi a fare qualcosa di simile al seguente:

// mycode.h 
// ....... 
class myclass { 
private: 
    static const uint16_t kMyClassConstant_ = 0xBEEF; 
// ....... 
}; 

Grazie in anticipo per l'aiuto.

+0

No, non c'è niente di sbagliato in questo. (Si spera che alcuni esperti di C++ non debbano correggermi :)) – xxbbcc

+0

È possibile ottenere risposte migliori su http://codereview.stackexchange.com. –

+2

Sì, è un modo normale di dichiarare una costante globale. –

risposta

38

Data la descrizione della situazione, direi che usare i membri static const è un buon approccio. In C++ 11 potresti volerlo cambiare in static constexpr per enfatizzare che è una costante in fase di compilazione, anche se nulla cambierà di conseguenza.

Se si fa riferimento a myclass::kMyClassContant_ da qualche parte nel codice in un modo che è rilevante ai sensi della regola a una definizione (odr), esp. in contesti che richiedono un riferimento (incluso il riferimento const), il compilatore si lamenterà che non esiste una definizione della costante. In questo caso non è sufficiente dichiararlo e inizializzarlo all'interno della classe. Questo può costringere a separare la dichiarazione e la definizione:

// mycode.h 
class myclass { 
private: 
    static const uint16_t kMyClassConstant_; 
}; 

// mycode.cpp 
const uint16_t myclass::kMyClassConstant_ = 0xBEEF; 

Per evitare la fatica di mantenere le dichiarazioni e le definizioni separati, alcune persone preferiscono dichiarare una funzione inline constexpr invece di una variabile reale:

// mycode.h 
class myclass { 
private: 
    static constexpr uint16_t kMyClassConstant_() 
    { return 0xBEEF; } 
}; 

Questo è un corretto work-around per molti dei problemi relativi a odr e non causa alcuna perdita di prestazioni. Se sia davvero utile dipende da quanto sia oneroso mantenere dichiarazioni e definizioni separate di una costante statica ordinaria. Se ti aspetti che le tue costanti non cambino mai mentre il tuo codice si evolve, è preferibile utilizzare costanti statiche ordinarie con definizioni separate. Ma se si modificano frequentemente le definizioni delle costanti, la necessità di ricompilare il file di definizione e ricollegarlo a tutte le parti rilevanti del progetto può far considerare la soluzione basata su funzioni come alternativa migliore.

Un commento finale sul tipo di dati: forzandolo a 16 bit utilizzando std::uint16_t può essere utile se è necessario memorizzare molti di questi valori in forma compatta. In caso contrario, le dimensioni effettive potrebbero non essere importanti, nel qual caso std::uint_fast16_t (che può essere maggiore di 16 bit) potrebbe essere migliore.

+7

Ho prestato attenzione alle vostre risposte in SO e, per quanto posso dire, sono le migliori in termini di chiarezza e precisione. Ciò contrasta con alcuni individui con un'alta "reputazione", che si appellano costantemente alla mistificazione piuttosto che rispondere alle domande. Congratulazioni (+1) – Ayrosa

+1

@ 411165 Grazie per l'apprezzamento :) Apprezzo la tua completezza nello studio dei post di SO (e di altri)! – jogojapan

+0

Questa risposta è abbastanza utile, ma non ho il ragionamento sulla ricompilazione. Penso che usando le funzioni dei membri inline (nella definizione della classe e quindi nel file di intestazione), ogni modifica ai valori richiederebbe _recompilation_ di tutte le unità di traduzione client (che includono quel file di intestazione), anche se non possono accedere direttamente a quelle private funzioni membro. D'altra parte con dichiarazioni e definizioni separate, la modifica dei valori modifica solo il modulo di implementazione, richiedendo una singola ricompilazione e solo il _relinking_ dei client. Questo è meno, non di più. –

5

Si potrebbe utilizzare caratteri morfologici per implementare questo:

#include <type_traits> 

class myclass { 
private: 
    typedef std::integral_constant<uint16_t , 0xBEEF> kMyClassConstant; 

    // ... 
}; 

utilizzato come myclass::kMyClassConstant::value.

Questo mostra lo scopo dell'implementazione di una costante integrale e impedisce di prendere accidentalmente un indirizzo della costante.