2010-10-08 502 views
2

我很難調試生產崩潰。只是想在這裏與人們確認語義。我們有一類像...Ctor初始化程序:自初始化會導致崩潰?

class Test { 
public: 
    Test() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

有人更改爲使用構造函數初始化,列出這是我們的代碼語義內合理正確的初始化。初始化的順序及其初始值是正確的。所以班級看起來像...

class Test { 
public: 
    Test() 
    : /*other inits ,,, */ m_str(m_str) 
    { 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

但是代碼突然開始崩潰!我將這個長長的列表分隔到這段代碼m_str(m_str)。我通過link text確認了這一點。

它是否有崩潰?標準對此有何評論? (它是未定義的行爲?)

+4

您正在初始化m_str與自己?你爲什麼做這個? – Starkey 2010-10-08 15:43:11

+1

未定義的行爲意味着它可以格式化您的c盤並安裝另一個操作系統。沒有人知道什麼特定的「未定義的行爲」意味着特定的環境。 – 2010-10-08 15:44:08

+0

別人的代碼,我會改變它,但需要從標準確認它確實是未定義的行爲,因爲它不會崩潰像POD類型'int我;我(i)'在初始化列表中 – Abhay 2010-10-08 15:46:14

回答

13

第一個構造相當於

Test() 
    : m_str() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 

就是由你來分配在構造函數中的時間,m_str已經被隱式地初始化爲空字符串。因此,賦予自我,儘管完全無意義和多餘,不會引起任何問題(因爲std::string::operator=(),正如任何寫得很好的賦值運算符應該檢查自賦值並且在這種情況下什麼也不做)。

但是,在第二個構造函數中,您正試圖初始化m_str自身在初始化程序列表中 - 此時尚未初始化。所以結果是未定義的行爲。

更新:對於原始類型,這仍然是不確定的行爲(導致垃圾值的字段),但它不會崩潰(通常是 - 見註釋下面的例外),因爲原始類型定義有沒有構造函數,析構函數並且不包含指向其他對象的指針。

對於不包含擁有所有權語義的指針成員的任何類型也是如此。 std::string特此證明不是這些中的一個:-)

+2

取決於「原始類型」的含義。即使將不確定值賦值給'int'原則上也會導致崩潰,因爲標準允許填充和陷阱表示形式爲'int'。你必須深入到'char'類型以便具有定義明確的初始化的可移植代碼。 – 2010-10-08 16:20:53

+0

@Alf即使對於'char'類型也是如此,這是未定義的行爲。只有當你引用一個'char'類型的指針時,它纔會被定義爲任何和所有可能的模式(因爲它然後從內存中讀取並且因爲'char'類型沒有陷阱)。請參閱http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#240上的最後一個註釋,*「CWG不認爲訪問未簽名的char可能仍會陷入在一個登記冊中分配,並需要重新評估所提出的決議。「*。 – 2010-10-08 19:58:57

+0

@Johannes:謝謝,我沒有想到! (但是委員會也沒有......)但是,我確信它是關於「ENIAC兼容性」的最後一個喘息,並且沒有實踐中的問題 - 至少,我真誠地希望如此! :-) – 2010-10-08 20:28:14

2

m_str構造在初始化列表中。因此,當你將它分配給它自己時,它不是完全構建的。因此,未定義的行爲。

(什麼是自賦值應該無論如何做?)

+0

我不知道,別人的代碼。如果確實是未定義的行爲,我需要確認。 Becoz它絕對不會爲POD類型崩潰。 – Abhay 2010-10-08 15:44:58

+4

你爲什麼需要確認?這是錯誤的代碼。只需修復它。 – Tim 2010-10-08 15:49:19

+0

@Tim:同意。但它的實時代碼,每天都會達到100萬次。我需要從更高的人那裏得到批准,誰說'它已經工作了很長時間...'所以我需要一個有說服力的參考。 – Abhay 2010-10-08 15:53:41

1

未定義行爲並不一定導致崩潰 - 它可以做任何事情,從繼續工作,就好像沒有問題,立即崩潰,做一些非常奇怪的事情,以後會導致看起來不相關的問題。經典的說法是它使「惡魔從你的鼻子裏飛出來」(又名「導致鼻子惡魔」)。有一次,這個階段的發明者有一個(非常酷的)網站,講述了在「DeathStation 9000」中引發未定義行爲的人開始的核戰爭。

編輯:從標準的確切措辭(§:1.3.12):

1.3.12未定義的行爲[defns.undefined]

行爲,例如可能在使用時會出現的錯誤的程序結構或錯誤的數據,對此,國際標準沒有要求。當國際標準忽略任何顯式行爲定義的描述時,也可能會出現未定義的行爲。 [注意:允許未定義 行爲的範圍從忽略完全具有不可預知的結果的情況,到在以文檔化的方式執行特定於環境(有或沒有 發佈診斷消息)的翻譯或程序執行期間的行爲,終止翻譯或執行(通過發佈 診斷消息)。

0

這是

之間
std::string str; 
str = str; 

std::string str(str); 

前者的作品一樣差(雖然這是廢話),後者則沒有,因爲它試圖複製結構來自尚未構造的對象的對象。

當然,要走的路將是

Test() : m_str() {} 
2

通過賦值原始的「初始化」是完全多餘的。

除了浪費處理器週期之外,它沒有造成任何傷害,因爲在賦值時m_str成員已經默認初始化了。

在第二個代碼片段中,默認初始化被覆蓋,以使用尚未初始化的成員來初始化它自己。這是未定義的行爲。而這完全沒有必要:只是刪除它(並且不要重新引入原來的時間浪費,只是刪除)。

通過調高編譯器的警告級別,您可能會收到有關此類警報以及類似的不良代碼的警告。

不幸的是,你遇到的問題不是這個技術問題,它更重要。這就像一家汽車工廠的工人對他們投放在新車品牌上的方形車輪提出質疑。那麼問題不在於方形車輪無法工作,而是很多工程師和經理都參與了決定使用看上去很方便的方形車輪,並且他們都沒有反對 - 他們中的一些人毫無疑問並沒有反對,我不明白方形輪不起作用,但我懷疑其中大多數人都害怕說出他們100%確定的東西。所以這可能是一個管理問題。我很抱歉,但我不知道該修復...

+0

在我的情況下也是如此。我想我可以說服他們使用C++標準作爲參考。等待引用..我沒有n3092.pdf或 – Abhay 2010-10-08 15:56:46

+0

的那個/ Internet的副本您可以從C++委員會頁面[http://www.open-std.org]下載最新的標準草案/ JTC1/SC22/WG21 /]。 – 2010-10-08 16:22:26