2010-02-09 16 views
5

Mi è stato detto che la variabile di riferimento deve essere inizializzata nell'elenco di inizializzazione, ma perché questo è sbagliato?inizializza il riferimento nell'elenco di inizializzazione

class Foo 
    { 
    public: 
     Foo():x(0) {  
     y = 1; 
     } 
    private: 
     int& x; 
     int y; 
    }; 

Perché 0 è un oggetto temporaneo? In tal caso, quale tipo di oggetto può essere associato? L'oggetto che può prendere un indirizzo?

Grazie!

risposta

15

0 non è un lvalue, è un valore. Non è possibile modificarlo, ma stai cercando di associare a un riferimento dove potrebbe essere modificato.

Se si effettua il riferimento const, funzionerà come previsto. Considera questo:

int& x = 0; 
x = 1; // wtf :(

Questo ovviamente è un no-go. Ma const& 's possono essere associati a temporanei (rvalues):

const int& x = 0; 
x = 1; // protected :) [won't compile] 

notare che il tempo di vita della temporanea termina al completamento del costruttore. Se si effettua statico-storage per il vostro costante, sarai al sicuro:

class Foo 
{ 
public: 
    static const int Zero = 0; 

    Foo() : x(Zero) // Zero has storage 
    { 
     y = 1; 
    } 
private: 
    const int& x; 
    int y; 
}; 
+0

L'esempio specifico nel suo costruttore però fallirà miseramente se dichiara la variabile membro 'const'. – Omnifarious

+1

@Onniversario: come mai? – GManNickG

+0

Cosa succede quando lo 0 temporaneo viene distrutto? Che ne dici se il suo indirizzo è preso e quello non controllato? – Omnifarious

0

Un riferimento vissuto a lungo deve essere legato a un lvalue. Fondamentalmente, come l'eloquentemente lo metti, un oggetto che ha un indirizzo definito. Se sono legati a un temporaneo, il temporaneo verrà distrutto mentre il riferimento fa ancora riferimento a esso e i risultati non sono definiti.

I riferimenti const di breve durata (variabili di funzione locali e argomenti di funzione) possono essere associati a provvisori. Se lo sono, è garantito che il temporaneo non venga distrutto fino a quando il riferimento non rientra nell'ambito di applicazione.

codice

Dimostrazione:

#include <iostream> 

class Big { 
public: 
    Big() : living_(true), i_(5) { // This initialization of i is strictly legal but 
     void *me = this;   // the result is undefined. 
     ::std::cerr << "Big constructor called for " << me << "\n"; 
    } 
    ~Big() { 
     void *me = this; 
     living_ = false; 
     ::std::cerr << "Big destructor called for " << me << "\n"; 
    } 

    bool isLiving() const { return living_; } 
    const int &getIref() const; 
    const int *getIptr() const; 

private: 
    ::std::string s_; 
    bool living_; 
    const int &i_; 
    char stuff[50]; 
}; 

const int &Big::getIref() const 
{ 
    return i_; 
} 

const int *Big::getIptr() const 
{ 
    return &i_; 
} 

inline ::std::ostream &operator <<(::std::ostream &os, const Big &b) 
{ 
    const void *thisb = &b; 
    return os << "A " << (b.isLiving() ? "living" : "dead (you're lucky this didn't segfault or worse)") 
      << " Big at " << thisb 
      << " && b.getIref() == " << b.getIref() 
      << " && *b.getIptr() == " << *b.getIptr(); 
} 

class A { 
public: 
    A() : big_(Big()) {} 

    const Big &getBig() const { return big_; } 

private: 
    const Big &big_; 
}; 

int main(int argc, char *argv[]) 
{ 
    A a; 
    const Big &b = Big(); 
    const int &i = 0; 
    ::std::cerr << "a.getBig() == " << a.getBig() << "\n"; 
    ::std::cerr << "b == " << b << "\n"; 
    ::std::cerr << "i == " << i << "\n"; 
    return 0; 
} 

E l'output:

Big constructor called for 0x7fffebaae420 
Big destructor called for 0x7fffebaae420 
Big constructor called for 0x7fffebaae4a0 
a.getBig() == A living Big at 0x7fffebaae420 && b.getIref() == -341121936 && *b.getIptr() == -341121936 
b == A living Big at 0x7fffebaae4a0 && b.getIref() == 0 && *b.getIptr() == 0 
i == 0 
Big destructor called for 0x7fffebaae4a0 
0

Beh, non si può mai cambiare, 0 può mai uguale qualcosa di diverso da 0.

provare

class Foo 
    { 
    public: 
     Foo(int& a):x(a) {  
     y = 1; 
     } 
    private: 
     int& x; 
     int y; 
    }; 

Alte in modo razionale, puoi farlo se il tuo riferimento è costante perché 0 può essere sempre uguale a zero

+0

Il riferimento diventa non valido una volta eseguito il costruttore. :/ – GManNickG

+0

No, il riferimento diventa non valido quando l'int passato al costruttore esce dall'ambito della funzione chiamante – Steve

+0

Sì, le risposte di modifica post sono piuttosto basse. Dovresti dire "ho aggiustato". no, "Ti sbagli." :( – GManNickG

Problemi correlati