2013-04-24 65 views
60

我在這裏做了一個測試,但輸出是一個沒有結束的循環,我不知道爲什麼。構造函數中的無限循環沒有for或while

其實,我正在做另一個測試,但是當我寫這個時,我不明白循環是怎麼發生的。它反覆輸出「ABC」。

#include <map> 
#include <string> 
#include <iostream> 

class test 
{ 
public: 
    std::map <int, int> _b; 
    test(); 
    test (std::map<int, int> & im); 
    ~test(); 
    }; 

test::test() 
{ 
    std::cout<<"abc"; 
    _b.clear(); 
    _b[1]=1; 
    test(_b); 
} 

test::test(std::map <int, int>& im) 
{ 
    std::cout<<im[1]; 
} 

test::~test() {}; 

int main() 
{ 
    test a; 
} 
+1

由於'test(_b)'的遞歸;'但我不確定原因。 – Roddy 2013-04-24 20:07:08

+0

@ Roddy-我只是想通了;詳情請參閱我的答案。 – templatetypedef 2013-04-24 20:09:14

+1

已刪除不重要的內容的清理版本:http://ideone.com/z0yc7Q – Yakk 2013-04-24 20:15:33

回答

94

這裏的問題是,編譯器解釋

test(_b); 

還不如代碼創建類型test傳遞參數_b的臨時對象,而是作爲一個名爲test_b變量聲明一個變量,使用默認的構造函數。因此,看起來像使用第二個構造函數創建臨時對象test的一段代碼是遞歸創建test類型的新對象並再次調用構造函數。

爲了解決這個問題,你可以給變量一個明確的名稱,如

test t(_b); 

這隻能解釋爲命名ttest類型的變量,使用第二個構造函數初始化。

我有從來沒有之前看過這個,我已經在C++編程多年。感謝您向我展示另一個該語言的角落案例!

有關官方解釋:根據C++ 03 ISO規範,§ 6。8:

有一個在語法的歧義涉及表達語句和聲明:表達式語句與函數式明確的類型轉換(5.2.3),作爲其最左邊的子表達式可以是不可區分的聲明,其中所述第一聲明符開始以(在這些情況下該語句是一個聲明。

(我的強調)。換句話說,任何時候C++可以解釋一個語句作爲是表達式(臨時目標鑄)或作爲變量聲明,它將選擇聲明。C++規範明確給出了

T(a);

作爲聲明的示例,而不是將a轉換爲T類型的內容。

這是C++的Most Vexing Parse - 看起來像一個表達式反而被解釋爲聲明。我以前看過MVP,但在這方面我從來沒有見過。

希望這會有所幫助!

+1

謝謝,這對於Google來說確實很難。 – Antimony 2013-04-24 20:11:26

+0

很好的答案!任何規範參考可用於證明/反駁編譯器正確解釋這一點? FWIW,codepad.org表現出相同的行爲 – Tom 2013-04-24 20:11:33

+0

啊,我找到了一個解釋。它被稱爲「最痛苦的解析」。順便說一下,在C++ 11中,您可以通過將其更改爲'test {_b}'來解決此問題。 (使用大括號而不是括號)http://en.wikipedia.org/wiki/Most_vexing_parse – Antimony 2013-04-24 20:16:03

0

問題從構造你再次調用構造器測試(_B)

測試::測試(){性病::法院< < 「ABC」; _ b.clear(); _b [1] = 1;測試(_B);}

這裏是發生了什麼

每次你打電話測試(_B)首先調用默認的構造函數測試::測試,它在原來的呼叫測試(_b)和循環繼續,直到堆棧溢出。

刪除默認的構造函數

+0

這是爲什麼調用默認構造函數?我想這會調用隱式轉換構造函數'test :: test(std :: map &)',因爲它顯式傳入'_b'。 – templatetypedef 2013-04-24 19:59:38

+0

它爲什麼調用默認的構造函數? C++不會鏈構造函數。 – Antimony 2013-04-24 19:59:54

+0

在C++中,你不應該調用另一個構造函數。例如,這與Java不同。 – OlivierD 2013-04-24 20:00:19

0

測試(_B)我不熟悉標準的特殊性,但它可能是調用構造函數內的構造是不確定的。因此它可能是編譯器依賴的。在這種特殊情況下,它會導致無限遞歸您的默認構造函數,而無需使用map參數調用您的構造函數。

C++ FAQ 10.3有一個帶有兩個參數的構造函數的例子。如果向第二個構造函數(如test(map, int))添加int參數,它會表現出某種正常行爲。

對於好的表單,我會簡單地將test::test(std::map <int, int>& im)更改爲test::testInit(std::map <int, int>& im)test(_b)testInit(_b)

+0

它調用另一個構造函數,但它不像以前一樣。我不明白爲什麼會導致這裏展示的無限遞歸。 – templatetypedef 2013-04-24 20:02:30

0

我敢肯定,你實際上並不是「調用構造函數」,因爲它們不是直接可調用的IIRC。法律語言必須與建設者不是命名功能 - 我沒有一個標準的方便副本,或者我可以引用它。我相信你在做什麼test(_b)創建一個未命名的臨時調用默認的構造函數。

+0

它不創建一個未命名的臨時文件並調用默認構造函數;相反,它創建一個名爲'_b'的變量並調用它的默認構造函數。查看我的答案瞭解更多詳情。 – templatetypedef 2013-04-24 20:10:05

+0

謝謝。我記得它做的不是確切的原因。我忘記了C/C++聲明規則是如何扭曲的。 – 2013-04-24 20:11:19