2013-05-10 124 views
1

只是偶然發現了這個問題,誰能解釋一下這裏發生了什麼?爲什麼這個成員變量沒有正確初始化?

struct Foo { 
    int i; 
    ~Foo() { 
     std::cout << i << std::endl; 
    } 
}; 

void bar() 
{ 
    Foo f; 
    f.i = 1; 
    f = Foo(); 
    f.i = 2; 
} 

我得到以下輸出:

-85899... (gibberish = "default" value for uninitialized int) 
2 

,我預計

1 
2 

爲什麼是它f.i = 1;似乎這裏有沒有影響?

+6

因爲f的析構函數,當你一個新的Foo值分配給它不會被調用。第一個輸出可能來自f = Foo()中臨時的析構函數。 – 2013-05-10 12:46:06

+3

@mfontanini沒有未定義的行爲。未初始化時'i'的值是不確定的。行爲是定義的,但不是確定的。完全不同的兩件事。 – pmr 2013-05-10 12:48:55

+0

你使用的是古老的編譯器嗎?自2003年以來(甚至可能是1998年),'Foo()'應該初始化臨時值,並將'i'設置爲'0'。 – 2013-05-10 13:20:03

回答

9

f.i = 1確實有效。它將成員i設置爲等於1。你只會看到如果對象被銷燬,因爲你在析構函數中輸出了這個值。

在行f = Foo();,你要創建一個臨時Foo對象,它與不確定的價值i,然後將其分配給對象f。這個臨時對象在行末被銷燬,打印出自己不確定的i。這是什麼給你-85899...輸出。

將不確定的值複製到對象f,但隨後使用值2覆蓋其成員i。在bar結束時,此對象被破壞,您將看到輸出2

+2

我不認爲還有什麼可以說的問題的代碼段! +1 – 2013-05-10 12:50:47

+1

可能值得一提的是,一個相當現代的,沒有bug的編譯器應該對臨時值進行初始化,並打印零點而不是垃圾。 – 2013-05-10 17:19:38

+0

@MikeSeymour C++不會使用簡單的類型對成員變量進行初始化,除非您告訴它。這是設計 - 對它們進行值初始化需要時間,C++假設您不關心(並且不想浪費那段時間),除非另有說明。特別是,該值被認爲是「不確定的」。 – 2013-05-13 17:19:32

11

因此,在第一次調用析構函數時銷燬的變量不是f,而是由Foo()創建的臨時變量。由於您沒有構造函數,因此i具有不確定的值。如果你要添加一個將i設置爲99999的構造函數,那麼你會看到你的析構函數的輸出。

void bar() 
{ 
    Foo f; // Construct f of type Foo 
    f.i = 1; // Set i to 1 in f. 
    f = Foo(); // Construct a temporary Foo object, copy it to f, 
       // then destroy the temporary object. 
    f.i = 2; // Set the newly copied f.i to 2. 
       // destroy f. 
} 
+0

+1解釋爲什麼 – 2013-05-10 12:47:20

+0

第二行是什麼原因? – Niko 2013-05-10 12:47:28

+2

@Niko - 你的範圍/程序結尾的f的析構函數。 – 2013-05-10 12:48:04

4

這樣的:

f = Foo(); 

創建一個新對象,並複製。那麼這個對象在這行結束時就遭到了破壞,但它並沒有被初始化。只是複製。

2

第一個輸出來自臨時調用的析構函數。默認賦值運算符不會調用任何析構函數,因此打印1的函數永遠不會被調用。

一些代碼來說明:

struct Foo { 
    int i; 
    // we emulate the default operator= generated by the compiler 
    // no check for self-assignment 
    Foo& operator=(const Foo& other) { this->i = other.i; } 
}; 

void bar() { 
    F f; 
    f.i = 1; 
    f = Foo(); // the i member of the temporary is indeterminate 
      // now f.i is indeterminate 
      // destroy the temporary 
    f.i = 2; 
} // end of scope, destroy f