2015-10-20 195 views
3

奇怪的std :: cout行爲我有一個方法返回一個字符串顯示爲錯誤消息。根據程序中發生此錯誤的位置,我可能會在顯示錯誤消息之前添加更多解釋。與const char *

string errorMessage() { 
    return "this is an error"; 
} 

// somewhere in the program... 
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << message << endl; 

(我存儲該消息作爲一個const char *因爲我將實際提供該誤差,其接受爲const char *參數的另一種方法)

在這一點上,輸出垃圾(不可打印的字符的安慰)。

所以我打了一下,發現如果不是我做的:

// somewhere in the program... 
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << ("Some extra info \n" + errorMessage()).c_str() << endl << message << endl; 

則顯示消息正確的兩倍。

爲什麼提供cout的額外參數會導致它按照我的意圖工作?

回答

11

("Some extra info \n" + errorMessage())臨時std::string。這意味着,聲明完成後,它的壽命已經結束。

cout << ("Some extra info \n" + errorMessage()).c_str() << endl 

作品,因爲在該點std::cout使用std::string其生命週期尚未結束。

<< message 

部分是未定義的行爲,雖然。純粹的運氣它的工作原理。

要解決此問題,需要將std::string的一生與無論是const std::string&延長或,因爲C++ 11,std::string&&

const std::string& str_const_ref = "Some extra info \n" + errorMessage(); 
std::string&& str_rvalue = "Some extra info \n" + errorMessage(); 

現在你可以在他們爲你想操作。

另一種方式是

std::string str = "Some extra info \n" + errorMessage(); 

但是,如果編譯器不會做一些Return Value Optimization,這將導致一個構造拷貝構造函數(< C++ 11,非常糟糕 )或移動構造函數(> = C++ 11,更好,但不必要)得到執行。


順便說一句,這個確切問題,甚至覆蓋 「的C++程序設計語言」 4 版!

在§10.3.4「臨時對象」,Stroustrup的先生寫道:

標準庫串具有構件c_str()(§36.3),它返回一個C語言風格的指針到零封端陣列字符 (§2.2.5,§43.4)。另外,運算符+被定義爲表示字符串 級聯。這些是字符串的有用設施。但是,在 組合它們可能會導致模糊的問題。例如:

void f(string& s1, string& s2, string& s3) { 
    const char* cs = (s1+s2).c_str(); 
    cout << cs; 
    if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') { 
     // cs used here 
    } 
} 

[...]甲臨時字符串對象被創建來保存s1+s2。接下來,從該對象提取指向C風格字符串的指針 。然後 - 在 的末尾表達式 - 臨時對象被刪除。然而,由c_str()返回的樣式字符串被分配作爲臨時 對象的一部分,持有s1+s2,並且該存儲不保證在 之後臨時銷燬。因此,cs指向釋放 存儲。輸出操作cout<<cs可能會按預期工作,但 這將是純粹的運氣[1]。編譯器可以檢測並警告這個問題的很多變體。 if -statement的問題有點微妙。 條件將按預期工作,因爲創建臨時存儲s2+s3的完整表達式是條件本身。 但是,在輸入受控語句 之前,該臨時銷燬已被銷燬,因此任何使用cs都無法保證正常工作。

所以,不要擔心你的C++技能。即使是C++聖經也解釋它。 ;-)

+0

啊,這是有道理的

我建議這樣做,而不是。這是事實,它總是在一個案件中工作,而從來沒有在其他案件中真正投擲我 – rbennett485

+0

@ rbennett485這樣的事情幾乎總是未定義的行爲或錯誤的宏觀。 :-) – Downvoter

+0

當你說'這樣的事情'時,你的意思是一個程序,有時按預期工作,有時不會? – rbennett485

3
const char* message = ("Some extra info \n" + errorMessage()).c_str(); 
cout << message << endl; 

errorMessage()返回一個臨時std::string對象
"Some extra info \n" + errorMessage()級聯創建另一個臨時對象。
取其c_str它返回一個指向其內部緩衝區(不是副本)的指針。
然後臨時對象被刪除,並且指針無效。
其他一切都是未定義的。它可能會給出正確的輸出,崩潰或做其他事情。

2

的問題是在這裏:

const char* message = ("Some extra info \n" + errorMessage()).c_str(); 

的errorMessage()將返回一個臨時的std :: string將走出去的範圍下一行運行之前。當你需要一個指針傳遞給底層的緩衝區,你可以使用

std::string message = "Some extra info \n" + errorMessage(); 

然後:

message.c_str();