2015-01-19 36 views
22

Ho codice C++ che si riduce a qualcosa di simile al seguente:inizializzare le variabili membro const

class Foo{ 
    bool bar; 
    bool baz; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr){ 
    const struct my_struct* s = complex_method(ptr); 
    bar = calculate_bar(s); 
    baz = calculate_baz(s); 
} 

vista semantico, le variabili membro bar e baz dovrebbe essere const, dal momento che non dovrebbe cambiare dopo l'inizializzazione. Tuttavia, sembra che per renderli tali, avrei bisogno di inizializzarli in una lista di inizializzazione piuttosto che assegnarli. Per essere chiari, capisco why Ho bisogno di fare questo. Il problema è che io non riesco a trovare un modo per convertire il codice in un elenco di inizializzazione senza fare una delle seguenti cose indesiderabili:

  • chiamata complex_method due volte (sarebbe male per le prestazioni)
  • Add il puntatore alla classe Foo (renderebbe la dimensione della classe inutilmente grande)

C'è un modo per rendere costanti le variabili evitando queste situazioni indesiderabili?

+0

Come parte, sei sicuro che 'complex_method' dovrebbe tornare con il puntatore? E se lo fa, non è necessario ripulirlo, il che suggerisce di usare 'std :: unique_ptr' per salvarlo? – Deduplicator

+0

Esatto, ho solo semplificato il codice effettivo per il frammento di cui sopra per brevità. – dooglius

+2

Evita di creare membri const. Se l'oggetto Foo non dovesse cambiare, rendere invece il vero oggetto const. –

risposta

25

Se potete permettervi un compilatore C++ 11, in considerazione delegating constructors:

class Foo 
{ 
    // ... 
    bool const bar; 
    bool const baz; 
    Foo(void const*); 
    // ... 
    Foo(my_struct const* s); // Possibly private 
}; 

Foo::Foo(void const* ptr) 
    : Foo{complex_method(ptr)} 
{ 
} 

// ... 

Foo::Foo(my_struct const* s) 
    : bar{calculate_bar(s)} 
    , baz{calculate_baz(s)} 
{ 
} 

Come consiglio generale, fare attenzione dichiarando i membri dei dati come const, perché ciò rende la tua classe impossibile da copiare, assegnare e spostare assegnare. Se si suppone che la classe venga utilizzata con semantica del valore, tali operazioni diventano desiderabili. Se questo non è il caso, puoi ignorare questa nota.

+2

UV, soprattutto per la nota alla fine. – Angew

8

Puoi usare costruttore di delegato in C++ 11:

class Foo{ 
public: 
    Foo(const void* ptr) : Foo(complex_method(ptr)) {} 

private: 
    Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {} 

private: 
    const bool bar; 
    const bool baz; 
}; 
11

Un'opzione è un costruttore delegato C++ 11, come discusso in altre risposte. Metodo compatibile con 03 Il C++ è quella di utilizzare un oggetto secondario:

class Foo{ 
    struct subobject { 
     const bool bar; 
     const bool baz; 
     subobject(const struct my_struct* s) 
      : bar(calculate_bar(s)) 
      , baz(calculate_baz(s)) 
     {} 
    } subobject; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr) 
    : subobject(complex_method(ptr)) 
{} 

È possibile effettuare bar e baz const, o fare il subobject const, o entrambi.

Se si fa solo subobject const, allora è possibile calcolare complex_method e assegnare ai bar e baz all'interno del costruttore di subobject:

class Foo{ 
    const struct subobject { 
     bool bar; 
     bool baz; 
     subobject(const void*); 
    } subobject; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr) 
    : subobject(ptr) 
{} 
Foo::subobject::subobject(const void* ptr){ 
    const struct my_struct* s = complex_method(ptr); 
    bar = calculate_bar(s); 
    baz = calculate_baz(s); 
} 

Il ragione che non si può mutare const membri all'interno di un corpo costruttore è che un corpo costruttore viene trattato come qualsiasi altro corpo funzione membro, per coerenza. Si noti che è possibile spostare il codice da un costruttore in una funzione membro per il refactoring e la funzione membro factored-out non richiede alcun trattamento speciale.

2

Se non si desidera utilizzare i costruttori deleganti newfangled (devo ancora occuparmi di versioni del compilatore che non ne sono a conoscenza) e non si desidera modificare il layout della classe, è possibile optare per una soluzione che sostituisce il costruttore con l'argomento const void * da una funzione membro statica che restituisce Foo, pur avendo un costruttore privato che prende l'output da complex_method come argomento (quest'ultimo molto simile agli esempi di costruttore delegante).La funzione membro statico esegue quindi il calcolo preliminare necessario che comprende complex_method e termina con return Foo(s);. Ciò richiede che la classe abbia un costruttore di copia accessibile, anche se la sua chiamata (nell'istruzione return) può essere probabilmente eliminata.

Problemi correlati