2010-02-09 130 views
5

我被告知引用變量必須在初始化列表中初始化,但爲什麼這是錯誤的?在初始化列表中初始化引用

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

因爲0是一個臨時對象嗎?如果是這樣,什麼樣的對象可以被引用綁定?可以接收地址的對象?

謝謝!

回答

15

0不是左值,它是右值。你不能修改它,但你試圖綁定到一個可以修改的引用。

如果您提供參考const,它將按預期工作。考慮一下:

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

這顯然是一個不行。但const&「可以綁定到臨時量(右值):

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

注意的是,臨時的生活時間是在構造函數中完成結束。如果你把靜態存儲你的不變,你將會很安全:

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

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

雖然構造函數中的具體示例如果聲明瞭成員變量'const',將會失敗。 – Omnifarious

+1

@Omnifarious:這是怎麼回事? – GManNickG

+0

臨時0被破壞時會發生什麼?如果它的地址被提取並且被解除引用,那麼如何? – Omnifarious

0

長住引用必須綁定到一個lvalue。基本上,正如你雄辯地說,一個具有明確地址的對象。如果它們被綁定到一個臨時的臨時將被銷燬,而引用仍然引用它,結果是未定義的。

短暫的const引用(局部函數變量和函數參數)可以綁定到臨時對象。如果是,那麼臨時保證在參考超出範圍之前不被銷燬。

示範代碼:

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

和輸出:

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

好吧,你永遠無法改變它,0可以將其他從未等於什麼比0

嘗試

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

Alte實際上,如果你的引用是恆定的,你可以這樣做,因爲0只能等於零

+0

構造函數完成後,該引用將變爲無效。 :/ – GManNickG

+0

不,當傳入構造函數的int超出調用函數的範圍時,引用變爲無效 – Steve

+0

是的,後編輯響應非常低。你應該提到「我修好了」。不是,「你錯了。」 :( – GManNickG