2010-03-22 2 views
5

Si consideri il seguente codice:È un comportamento non definito nel caso delle chiamate di funzioni private nella lista di inizializzazione?

struct Calc 
{ 
    Calc(const Arg1 & arg1, const Arg2 & arg2, /* */ const ArgN & argn) : 
     arg1(arg1), arg2(arg2), /* */ argn(argn), 
     coef1(get_coef1()), coef2(get_coef2()) 
    { 
    } 

    int Calc1(); 
    int Calc2(); 
    int Calc3(); 

private: 
    const Arg1 & arg1; 
    const Arg2 & arg2; 
    // ... 
    const ArgN & argn; 

    const int coef1; // I want to use const because 
    const int coef2; //  no modification is needed. 

    int get_coef1() const { 
    // calc coef1 using arg1, arg2, ..., argn; 
    // undefined behavior?  
    } 
    int get_coef2() const { 
    // calc coef2 using arg1, arg2, ..., argn and coef1; 
    // undefined behavior? 
    } 

}; 

struct Calc non è completamente definita quando chiamo get_coef1 e get_coef2 È questo il codice valido? Posso avere UB?

+0

Funziona ... ma ti metti nei guai. Cosa succede quando arriva il manutentore, aggiungi 'argZ' dopo i coef e poi usa' argZ' nel calcolo? Avrai il tuo UB ... –

risposta

8

12.6.2.8: Le funzioni membro (incluse le funzioni membro virtuale, 10.3) possono essere chiamate per un oggetto in costruzione. Allo stesso modo, un oggetto in costruzione può essere l'operando dell'operatore typeid (5.2.8) o di un dynamic_cast (5.2.7). Tuttavia, se queste operazioni vengono eseguite in un iniziatore di Ctor (o in una funzione chiamata direttamente o indirettamente da un iniziatore di Ctor) prima che tutti gli inizializzatori di mem per le classi di base siano completati, il risultato dell'operazione non è definito.

In questo modo è possibile inizializzare i membri della classe, ma non le classi di base. E, come altri hanno sottolineato, dovresti essere a conoscenza dell'ordine di inizializzazione dei membri, se la tua funzione utilizza alcuni dei loro valori.

+0

+1 perché è più pertinente a ciò che l'OP voleva veramente sapere. –

3

Poiché le variabili su cui si basa il calcolo sono già inizializzate al momento della chiamata, non deve essere un comportamento non definito. Vedere la domanda this per informazioni correlate.

+3

E tieni presente che i membri vengono inizializzati nell'ordine in cui appaiono le loro dichiarazioni, non nell'ordine in cui appaiono nell'elenco di inizializzazione. Qui è lo stesso, ma si tenga presente che semplicemente mettendo 'const int coef1;' sopra 'const Arg1 & arg1;' (e mantenendo lo stesso elenco di inizializzazione) * creerebbe * UB. –

+0

La mia domanda non riguarda l'ordine di inizializzazione. L'ordine in cui appaiono le loro dichiarazioni è ok. Chiedo delle funzioni 'get_calc1'. Il puntatore 'this' è valido, ad esempio? 'arg2' è inizializzato, sì, vero. Ma posso accedere a 'arg2' in' get_calc1'? –

+1

È perfettamente corretto chiamare le funzioni membro che fanno riferimento solo agli attributi dei membri inizializzati. –

0

Non è non indefinito, ma è necessario essere assolutamente sicuri che quelle funzioni membro utilizzino solo valori inizializzati. Si noti inoltre che i valori sono inizializzati nell'ordine in modo che vengano visualizzati nella classenon nell'ordine in cui appaiono nell'elenco di inizializzazione. Ad esempio:

struct Foo 
{ 
    int a, b; 
    int c; 
    Foo(): c(1), a(1), b(1) {} 
}; 

In tale costruttore, le variabili sono inizializzate nell'ordine a, b, c poi, l'ordine nell'elenco significa niente. Quindi, se si desidera inizializzare il valore di a utilizzando alcuni calcoli su b e c, sarà necessario spostare la dichiarazione di a in un punto successivo a quello di b e c.

+0

Thnks. Ma la mia domanda non riguarda l'ordine di inizializzazione. Potresti notare che l'ordine era tutto a posto. –

+0

Ho risposto così nelle prime parole. Non è indefinito, a patto che l'ordine sia giusto, che è nel tuo caso, quindi stai bene. –