2010-03-01 17 views
5

In primo luogo, la mia ultima codifica è Java e non voglio "scrivere Java in C++".C++ equivalente per dati membro java final

Ecco l'accordo, devo creare una classe immutabile. È abbastanza semplice. L'unico problema è che ottenere i valori iniziali è un po 'di lavoro. Quindi non posso semplicemente chiamare le inizializzazioni per inizializzare i miei membri.

Quindi qual è il modo migliore per creare una classe del genere? E come posso esporre le mie proprietà immutabili/definitive al mondo esterno negli standard C++?

ecco una classe di esempio:

class Msg { 
    private: 
     int _rec_num; 
     int _seq; 
     string text; 
    public: 
     Msg(const char* buffer) { 
      // parse the buffer and get our member here... 
      // ... lots of code 
     } 

     // does this look like proper C++? 
     int get_rec_num() { return _rec_num; } 
    }; 

risposta

7

C++ offre alcuni buoni meccanismi per rendere la vostra classe immutabile. Che cosa si deve fare è:

  • dichiarano tutti i metodi pubblici (e forse protette) const
  • dichiarare (ma non definire) operator= come privati ​​

Questo farà sì che gli oggetti non possono essere modificati dopo che sono stati creati. Ora puoi fornire l'accesso ai tuoi membri dei dati ora immutabili in qualsiasi modo, utilizzando metodi const. Il vostro esempio guarda a destra, a condizione che si rendono const:

int get_rec_num() const { return _rec_num; } 
+0

in realtà dovrebbe essere "const int get_rec_num() const ..." - Hassan Syed –

+3

Non fa differenza, perché per i tipi built-in, non esiste un valore costante. Non puoi mai scrivere 'get_rec_num() = 7', non importa se il tipo restituito è' int' o 'const int'. – fredoverflow

0

Su finalizzatori

Non c'è nessuno, si deve emulare. O utilizzando una funzione di pulizia o avendo tutte le risorse incapsulate nelle classi RAII. Il compilatore posizionerà dei macchinari statici nella tua applicazione per chiamare i distruttori sulle tue classi RAII --i.e., Quando escono dall'ambito, le risorse vengono rilasciate attraverso il distruttore.

Su immutabilità e inizializzazione

Generalmente se qualcosa è immutabile e const-correct la classe avrà tutti i suoi membri come const e l'unica volta che si arriva a "scrivere" per loro è quando la classe viene inizializzato. Tuttavia nel tuo caso potrebbe non essere possibile.

Suggerisco di raccogliere tutte le risorse e inizializzare la classe (tramite un costruttore non predefinito con membri const) una volta che li si dispone. L'altra alternativa (che non dimoro) è quella di avere una funzione mutatore che "afferma" di essere const corretta ma scrive sui valori const per un'inizializzazione di post-costruzione una tantum.

+0

Mi sbaglio o un Finalizzatore è solo un distruttore paralizzato che si chiama alla fine? (Supponendo che si utilizzi una buona gestione della memoria usando oggetti di preferenza o puntatori intelligenti secondo necessità). –

+0

@martin Penso che abbia senso - zucchero sintattico; Ho appena avuto questa rivelazione mentre stavo scrivendo la risposta. Tuttavia, è più facile da programmare rispetto a venire a conoscenza di RAII. Quando ero un programmatore più giovane, desideravo un finalizzatore in C++. –

+0

@ Martin: in Java intendi? Se è così, allora un finalizzatore non è un distruttore menomato che alla fine viene chiamato. È uno pseudo-distruttore che * può o non può * essere chiamato alla fine. E non è semplicemente "storpiato" - in realtà può fare cose che un distruttore C++ non può (validamente) fare, il più importante e fastidioso di quale sia che possa resuscitare l'oggetto che è finalizzato, o altri oggetti finalizzati a cui questo oggetto si riferisce . –

1

Sei sulla buona strada: usa getter per tutto, e senza alcun setter, la tua classe è effettivamente immutabile.

Tuttavia, non dimenticare alcuni dei casi d'angolo: è possibile dichiarare il metodo operator =() come privato e non implementarlo in modo che qualcuno non possa ignorare l'oggetto con l'operatore di assegnazione generato dal compilatore predefinito, ecc. .

1
// does this look like proper C++? 
    int get_rec_num() { return _rec_num; } 

Si dovrebbe usare

int get_rec_num() const { return _rec_num; } 

(vedi const che permette di chiamare il membro sugli oggetti const).

+0

in realtà dovrebbe essere "const int get_rec_num() const ..." –

+2

Qual è la differenza se restituisce per valore? La sua classe sarà immutabile finché non ci sarà ritorno per riferimento. – pmr

+0

La sua buona convenzione, e se la evidenziamo ora l'OP impara un altro concetto. –

3

Contrassegnerei il membro immutabile come "const" e assegnargli un valore nell'elenco di inizializzazione del costruttore.

Vorrei anche analizzare il buffer fuori dalla classe e passare la stringa al costruttore.

Qualcosa di simile a questo:

class Msg { 
    private: 
    int _rec_num; 
    int _seq; 
    const std::string text; 
    public: 
    Msg(const std::string& str) : 
     text(str) 
    { 

    } 

    // does this look like proper C++? 
    int get_rec_num() const { return _rec_num; } 
}; 

// parse the buffer and get our parameter somewhere else 

NB:

Si dovrebbe fare in qualsiasi funzioni membro che non cambiano lo stato dei vostri interni di classe come 'const'; in quanto ciò ti consentirà di chiamarli con oggetti const.

È consigliabile evitare di utilizzare un file std :: string nei file di intestazione; come chiunque includa la tua intestazione ha questo 'uso' forzato su di loro.

+0

Personalmente non mi dispiace che i costruttori svolgano un lavoro significativo. In questo caso, dove la classe è immutabile e quindi tutte le funzioni membro saranno comunque 'const', penso che il vantaggio di contrassegnare le variabili membro' const' sia abbastanza trascurabile, e superata in maniera massiccia dallo svantaggio che non è possibile per fare in modo che il costruttore faccia qualcosa di utile. Immagino che dipenda da quanto piccole ti piacciono le tue lezioni, però - è un buon design per una classe essere responsabile di due cose (memorizzare un valore e anche ottenere quel valore da un buffer)? –

0

Per rendere una variabile immutabile è necessario utilizzare la parola chiave const esempio const int _rec_num. Le variabili Const possono essere inizializzate solo tramite un initialisation list, che viene chiamato prima di qualsiasi codice nel costruttore. Ciò significa che non è possibile eseguire alcuna elaborazione nel costruttore che imposta le variabili membro const.

Hai due modi per aggirare questo, per prima cosa puoi creare un'altra classe interna che prende un buffer e lo analizza nelle tue variabili. Mettere una versione const di questo nella vostra classe di MSG e mettere questo nella lista di inizializzazione:

class MsgInner 
{ 
    public: 
    int _rec_num; 

    Msg(const char* buffer) { 
     // Your parsing code 
    } 
}; 

class Msg 
{ 
    public: 
    const MsgInner msg; 

    Msg(const char* buffer) : msg(buffer) 
    { // any other code } 
}; 

Questo forse non è così 'standard', ma è un altro punto di vista. Altrimenti puoi anche farlo come suggerito dalle altre risposte con i metodi get.

0

Prima di tutto, è possibile inizializzare i membri in modo efficiente e in fase di costruzione anche se sono stati dichiarati come const (che non è né necessario né consigliato).

Vorrei ancora suggerire che di dividere questa classe in due classi distinte (pseudo-codice):

// Msg: pure data object; copy constructible but not modifiable. 
class Msg 
{ 
public: 
    Msg(int rec_num, ...) 
    : rec_num_(rec_num) 
    ... 
    {} 

    int rec_num() const 
    { return rec_num_; } 
    ... 
private: 
    // prevent copying 
    Msg& operator=(Msg const&); 
private: 
    int rec_num_; 
}; 

// MsgParser: responsible for parsing the buffer and 
// manufacturing Msg's. 
class MsgParser 
{ 
public: 
    static Msg Parse(char const* buffer) 
    { 
    ... parse ... 
    return Msg(...); 
    } 
}; 

// Usage 
Msg const msg = MsgParser::Parse(buffer); 

Questo anche ben separa le preoccupazioni di detenzione e l'analisi dei dati in classi separate.