2010-02-09 4 views
5

On m'a dit que la variable de référence doit être initialisée dans la liste d'initialisation, mais pourquoi cela est-il faux?initialiser la référence dans la liste d'initialisation

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

Parce que 0 est un objet temporaire? Si oui, quel type d'objet peut être lié? L'objet qui peut prendre une adresse?

Merci!

Répondre

15

0 n'est pas une lvalue, c'est une valeur. Vous ne pouvez pas le modifier, mais vous essayez de lier à une référence où il pourrait être modifié.

Si vous faites votre référence const, cela fonctionnera comme prévu. Considérez ceci:

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

Ceci est évidemment un non-go. Mais s » const& de peuvent être liés à (rvalues) temporaires:

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

Notez que la durée de vie est de temporaire a pris fin à la fin du constructeur. Si vous faites de stockage statique pour votre constante, vous serez en sécurité:

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

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

L'exemple spécifique dans son constructeur échouera encore lamentablement s'il déclare la variable membre 'const'. – Omnifarious

+1

@Omnifarious: Comment ça? – GManNickG

+0

Que se passe-t-il lorsque le 0 temporaire est détruit? Que diriez-vous si son adresse est prise et que déréférencé? – Omnifarious

0

A doit être liée référence à une longue durée de vie lvalue. Fondamentalement, comme vous le dites si éloquemment, un objet qui a une adresse définie. Si elles sont liées à un temporaire, le temporaire sera détruit pendant que la référence le référencera encore et les résultats ne sont pas définis.

Les références const de courte durée (variables de fonction locales et arguments de fonction) peuvent être liées à des temporaires. Si c'est le cas, le temporaire est garanti de ne pas être détruit tant que la référence n'est pas hors de portée.

Code de démonstration:

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

Et la sortie:

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

Eh bien, vous ne pouvez jamais changer, 0 ne peut jamais égale autre chose que 0.

essayer

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

Alte rnativement, vous pouvez le faire si votre référence est constante car alors 0 ne peut jamais être égal à zéro

+0

La référence devient invalide une fois le constructeur terminé. :/ – GManNickG

+0

Non, la référence devient invalide lorsque l'int passé dans le constructeur sort de la portée dans la fonction d'appel – Steve

+0

Oui, les réponses post-édition sont plutôt faibles. Vous devriez mentionner "J'ai réparé." pas, "Vous avez tort." :( – GManNickG

Questions connexes