2010-02-09 13 views
5

Me dijeron que la variable de referencia debe inicializarse en la lista de inicialización, pero ¿por qué está mal?inicializar la referencia en la lista de inicialización

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

¿Porque 0 es un objeto temporal? Si es así, ¿qué tipo de objeto puede referenciar? El objeto que puede tomar una dirección?

Gracias!

Respuesta

15

0 no es un lvalue, es un valor. No puede modificarlo, pero está tratando de vincularlo a una referencia donde podría modificarse.

Si hace su referencia const, funcionará como se esperaba. Considere esto:

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

Esto, obviamente, es un no-go. Pero const& 's se pueden unir a los temporales (rvalues):

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

en cuenta que el tiempo de vida del temporal se terminó al finalizar el constructor. Si comete-estática para su almacenamiento constante, estarás a salvo:

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

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

El ejemplo específico en su constructor aún fallará miserablemente si declara la variable miembro 'const'. – Omnifarious

+1

@Omnifarious: ¿Cómo es eso? – GManNickG

+0

¿Qué sucede cuando se destruye el 0 temporal? ¿Qué tal si se toma su dirección y se desreferencia? – Omnifarious

0

Una referencia de larga vida debe estar enlazado a un lvalue. Básicamente, como lo dices con tanta elocuencia, un objeto que tiene una dirección definida. Si están vinculados a un temporal, el temporal se destruirá mientras la referencia todavía hace referencia a él y los resultados no están definidos.

Las referencias de corta duración const (variables de función local y argumentos de funciones) pueden vincularse a las temporales. Si lo son, se garantiza que el temporal no se destruirá hasta que la referencia salga del alcance.

código

Demostración:

#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; 
} 

Y la salida:

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

Bueno, nunca se puede cambiar, 0 puede nunca es igual otra cosa que no sea 0.

tratar

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

Alte rnativamente, puede hacer esto si su referencia es constante porque entonces 0 solo puede ser igual a cero

+0

La referencia deja de ser válida una vez que el constructor finaliza. :/ – GManNickG

+0

No, la referencia se vuelve inválida cuando el int pasado al constructor sale del ámbito en la función de llamada – Steve

+0

Sí, las respuestas de edición de publicación son bastante bajas. Deberías mencionar "Me arreglé". no, "estás equivocado". :( – GManNickG

Cuestiones relacionadas