2013-03-20 6 views
8

Quando più proprietà const di una classe C++ dipendono da alcuni calcoli intermedi, qual è il modo più semplice per inizializzarle?Il modo più semplice per inizializzare più proprietà const relative in un costruttore?

Ad esempio, come si corregge il costruttore per la classe seguente?

class MyClass { 
public: 
    const int a; 
    const int b; 

    MyClass() { 
     int relatedVariable = rand() % 250; 
     a = relatedVariable % 100; 
     b = abs(relatedVariable - 150); 
    } 
}; 
+0

L'espressione per 'relatedVariable' può essere resa esterna alla classe? In modo che tu possa passarlo come parametro nel costruttore. – ulidtko

+1

Cerca negli elenchi di inizializzazione in C++ – tcannon91

risposta

10

Con C++ 11, si può semplicemente utilizzare un costruttore delegando:

class MyClass 
{ 
public: 
    const int a; 
    const int b; 

private: 
    MyClass(int relatedVariable) 
     : a(relatedVariable % 100), 
     b(abs(relatedVariable - 150)) {} 

public: 
    MyClass() : MyClass(rand() % 250) {} 
}; 
1

Introdurre una classe intermedia che fa il calcolo:

class ConstCalc { 

    public: 
    ConstCalc(int related) : rv(related){} 

    int a() const { return rv % 100; } 
    int b() const { return abs(rv - 150) ; } 

    private: 
    const int rv; 
}; 

class MyClass { 
public: 
    const int a; 
    const int b; 

    MyClass(const ConstCalc c) : a(c.a()), b(c.b()) { 
    } 
}; 
2

Si potrebbe fare qualcosa di simile - non bella, ma dovrebbe fare il trucco:

class MyClass { 
public: 
    const int a; 
    const int b; 
    static int relatedVariable; 
    MyClass() : 
     a(setRand()), 
     b(relatedVariable) {} 
    static const int setRand() 
    { 
     relatedVariable = rand() % 250; 
     return relatedVariable; 
    } 
}; 
int MyClass::relatedVariable = 0; 
+1

Perché separare la funzione per 'rand()% 250'? Potresti usarlo direttamente nell'inizializzatore. – ulidtko

+2

Questo non è protetto da thread. Probabilmente non importa, ma solo FYI. – Wug

2

Questo sarà un po lavoro per quelli di noi che preferiscono essere meno avanzati nella loro codifica:

class MyClass { 
public: 

    int iamStupid;  /* K.I.S.S. */ 

    const int a; 
    const int b; 

    MyClass() 
     : iamStupid(rand() % 250) 
     , a(iamStupid % 150) 
     , b(abs(iamStupid - 150)) 
    {} 
}; 

Il membro aggiuntivo presenta un sovraccarico non necessario, che può essere o non essere significativo per l'attività in corso. OTOH, il codice è semplice.

Ricordati di dichiarare iamStupidprimaa e b! (Vedi commenti)

+3

[Le variabili membro vengono inizializzate nell'ordine della loro dichiarazione] (http://stackoverflow.com/a/1242845/201270). Pertanto questo inizializzerebbe 'a' e' b' prima di inizializzare 'relatedVariable', che tipo di sconfigge il suo scopo. – Grizzly

+1

@Grizzly, corretto. Avrei dovuto dichiarare 'relatedVariable' prima di' a' e 'b'. – ulidtko

+0

@underscore_d modificato. – ulidtko

5

Ecco una soluzione rotonda con i costruttori delega:

class MyClass 
{ 
    MyClass(int aa, int bb) : a(aa), b(bb) { } 

    static MyClass Maker() { int x = /* ... */; return MyClass(x * 2, x * 3); } 

    int const a; 
    int const b; 

public: 
    MyClass(MyClass const &) = default; 
    MyClass() : MyClass(Maker()) { } 
}; 
+0

'error: digitare 'MyClass' non è una base diretta di 'MyClass''. – ulidtko

+0

@ulidtko: prova a compilare con un compilatore corrente (come in bleeding edge) e C++ 11 attivato per tale costruttore delegante. – Grizzly

+1

@ulidtko: utilizza le funzionalità di C++ 11. Il tuo compilatore potrebbe non supportare quelli. –

1

Const è un contratto tra utente e implementor di una classe. Indica che l'utente della classe non deve modificare le variabili membro, fornendo così un design di oggetto immutabile. Va bene per un costruttore che altrimenti inizializzi quello stato. Detto questo, potrebbe essere meglio nasconderlo dietro un qualificatore di accesso privato e fornire accessor che consentano la sola lettura. Il modo corretto per rimuovere temporaneamente const-ness è utilizzare il const_cast <>.

class MyClass { 
public: 
    const int a; 
    const int b; 

MyClass() : a(0), b(0) { 
    int relatedVariable = rand() % 250; 
    const_cast<int&>(a) = relatedVariable % 100; 
    const_cast<int&>(b) = abs(relatedVariable - 150); 
} 

};

+0

C'è qualche impatto sulle prestazioni per questa soluzione? Il compilatore non supporrà ora che a e b non siano realmente costanti? –

+2

È improbabile che si notino cambiamenti delle prestazioni. "const-ness" all'interno di una classe è una nozione in fase di compilazione. – jbruni

+3

@jbruni, sei sicuro che sia strettamente legale? IIRC 'const_cast' è solo ben definito se l'oggetto stesso non è' const' (cioè puoi lanciare un 'const T &' a 'T &' solo se il riferimento si riferisce ad un oggetto non originariamente dichiarato 'const') –

-1

È possibile creare private a e b e fornire ai getter l'accesso ai propri valori dall'esterno della classe.

class MyClass 
{ 
private: 
    int a, b; // private 
public: 
    int getA() { return a; } 
    int getB() { return b; } 

    MyClass() 
    { 
     int relatedVariable = rand() % 250; 
     a = relatedVariable % 100; 
     b = abs(relatedVariable - 150); 
    } 
}; 

Oppure, si potrebbe utilizzare inizializzatori subobject e memorizzare nella cache il numero casuale in qualche modo. Attivare l'ottimizzazione potrebbe anche rimuovere la variabile temporanea nel testo del programma generato.

class MyClass 
{ 
private: 
    int temp; // this is a workaround 
public: 
    const int a; 
    const int b; 

    MyClass() : temp(rand() % 250), a(temp % 100), b(abs(temp - 150)) {} 
}; 

Remember that subobject construction happens in the order that members are declared in the class, and that the order of subobjects in the initialization list is ignored.

In alternativa, si può essere pigri, e solo memorizzare il numero iniziale casuale, e generare a, b su richiesta.

+1

* "probabilmente rileverà il calcolo sovrapposto" * - non lo farà, perché 'rand()' ha effetti collaterali. – ulidtko

+0

Ma 'rand()' viene chiamato solo una volta. Qualsiasi metodo di memorizzazione nella cache del risultato produrrà lo stesso risultato, indipendentemente da quale bizzarro trucco viene utilizzato per farlo. La memoria 'a, b' non sarà mappata nella memoria di sola lettura, e ciò che l'ottimizzatore fa è la sua stessa prerogativa. Ma capisco cosa intendi e lo riformulerò. – Wug

2

Nel caso in cui si è bloccato con un antico compilatore che non supporta costruttori delega, ecco lo stesso approccio adattato per il versione precedente:

class MyClassBase { 
public: 
    const int a; 
    const int b; 
    MyClassBase(int a, int b) : a(a), b(b) {} 
}; 

class MyClass : public MyClassBase { 
    static MyClassBase Maker() { 
     int x = rand() % 250; 
     return MyClassBase(x % 100, abs(x - 150)); 
    } 
public: 
    using MyClassBase::a; 
    using MyClassBase::b; 

    MyClass() : MyClassBase(Maker()) { } 
}; 
Problemi correlati