(譯者注:我從網上看到這篇文章的原文,非常喜歡作者的寫作風格,於是就利用業余時 間將它翻譯出來,並貼到網上,希望大家可以從中受益,因為我沒有和作者或者這篇文章的 版權所有者聯系以取得這篇文章的版權,所以這篇中譯文的版權不應歸我所有,而且我未從 中獲得任何利益!而且,我對原作者及其版權所有者的敬仰有如濤濤江水,所以我絕沒有侵 犯原作者的任何意圖。當然,要說“利益”還是有的,就是我加深了如下C++的知 識:只能被綁定到一個常量的、非易失的引用上,左值卻沒有這樣的限制;函數返回的引用是 左值。而且不管怎麼說,如果一個對象的構造函數中引用了的臨時對象,而對象在該構造函數 構造結束時就消逝了的話,那麼該對象就成了一個“野引用”了。如果我理解得 有錯的話,請一定告訴我一聲,謝謝,[email protected]。還有 Herb Sutter 先生 的《more C++ Exceptional》中文版已經由華中科技大學出版社出版,小弟竊喜著買了一本 ,沈陽三好街的大松科技書店進新書的速度快得驚人,都是8折,不知道還有沒有更便宜的地 方:)
假日前不久的那幾天,我就那麼一次沒有感覺到最後期限(deadline )的壓力― ―我正在做的這個項目都已經按計劃完成了。
我選擇了最喜歡的消遣方式,撇掉了 source repository(譯者:源碼智囊團?不懂,是一款分析源碼的軟件嗎?)。我經常在研讀 別人代碼時,學習一些他們的技巧(對我來說是新的),以及學習如何避免他們代碼中的錯 誤。
class T
{
public:
T & ref() { return *this; }
};
void f( T & );
int main()
{
f( T().ref() );
}
開始時,我沒有理解ref()函數的重點,因此我刪除了對它的調用――我認為下邊這 樣的代碼應該可以正常運行:int main()
{
f( T() );
}
然而,當我編譯時,編譯器提示有個錯誤:將非常量引用綁定到一個臨時對象。我輕拍著前 額――當然了,這種綁定是不允許的。回想起第一次遇到這類錯誤的時候the Guru(譯者: 下文按領袖譯)給我的解釋。
她說道:“禁止這種綁定的一個原因是為了防止 狡猾的bug,我的孩子,看看下面的情況”class U
{
// ... 任 何代碼 ...
};
void takesAndModifiesU( U & u )
{
// 執 行操作以修改u的狀態
}
class V
{
public:
operator U();
};
void g()
{
V v;
// ...
takesAndModifiesU( v );
// ...
}
“如果允許那種綁定,編譯器將調用轉換操作符operator(), 創建一個臨時的U類的對象。這個匿名的臨時對象將被傳遞到takesAndModifiesU,被修改, 接著在函數調用完成之後被丟棄。原來的對象v,不會被改變――這會讓這段代碼的作者困惑 不解的。”
然而,我真的困惑了。我不能理解原來的語句f(T().ref());為何 能能夠編譯――那不也是將一個非常量引用綁定到一個臨時對象上了麼?
“我 的孩子,你必須學會多思考一步”,領袖(The Guru)的聲音從我耳邊傳來,而不是記憶 中,讓我著實吃了一驚,“考慮一下左值和右值。神聖的標准(ISO C++ 嗎?)告訴我 們可以用形式為T()的顯示的類型轉化創建一個右值。一個右值只能被一個常量的、非易失 (non-volatile)的引用綁定,但是一個左值沒有這樣的限制。而且,函數返回一個引用的話 ,那麼返回值就是左值。因此,編譯器能夠將綁定ref()綁定到一個非常量引用。 ”
“這樣呀,只有我調用某一個返回值是引用的函數,就可以了吧, ”我回答道,“嗨――賦值操作符返回一個引用,因此我可以這樣寫吧f( T() = T() );不錯吧!”我熱情洋溢。“我能想到這個技巧的許多用處喲。 ”
“小心,我的孩子。這種不常見的技巧可能是很危險的,不可以輕易使 用的。事實上,我至少可以想到一種情況:是關於對象生命的,這樣的話可能會導致未定義 的行為。”
“你是指...”我提示了一句。
“吃完午飯 之後再說吧,”領袖平靜地答道。我看見遠處的幾個同事正在准備去吃午飯。我一把抓 起外套,加入了他們,接著,我們向當地的一家飯館走去。
不知何故,午餐時我們一 直控制著不去談論關於購物的話題。討論多集中於我們最感情趣的假日電影,比較《三十四 號街的神谕》重拍前後的不同,還有Alistair Sim 和 Patrick Stewart 哪一個更加吝啬。 (我選了Stewart。)鮑勃對我此舉感到奇怪,然而――我認為他最喜歡的角色會是Grinch, 但是他卻非常喜歡《美麗人生》。
當我吃完午飯回去時,我心滿意足。我坐在桌子前 ,開始集中精神考慮領袖一會兒會說些什麼。最後,我努力地保持著清醒,並寫下了如下代 碼:class U
{
T& t_;
public:
U( T & t ) : t_( t ) { }
};
{ // ... 一些域塊 ...
U u( (T() = T()) );
// ...
}
當對象u完成構造時,臨時對象的生命就終結了,對象u將成為一個野引用(譯 者:因為有人將被釋放後但是沒有標示為NULL的指針稱作“野指針”)。
就好像T的對象從來都沒有存在一樣,我沉思著疑惑著。
“好吧,喬治, ”我聽到領袖的聲音,“你得到你想要的了。你永遠不會出生了。 ”
“啊?誰是喬治?”我看著領袖。
“就是你呀, ”領袖回答道。我一看到她我就知道我我正在做夢――在夢中,你自己知道自己是在做 夢,但是你確不得不深入其中。“你就是喬治·貝利。你創建的對象從來都沒有 存在過。我的工作就是在你處理未定義行為時,給你看看會發生什麼事情。”
我看見遠處的一條標語:“歡迎你,鮑勃維爾”
“鮑勃維爾? ”我問道,同時擔心著答案。
“這座城市正在被你的仇恨所融化,鮑勃, ”領袖說。“他也是這所銀行的主人,這個軟件部門的領導。來,我們沿著大街 走走吧。”(譯者:非常抱歉,這段話我不會翻譯,僅按字面意思)
當我們正 沿街走著時,我看到了無法形容的恐懼――單片集成代碼塊,變量名i和j,還有商業廣告 “這裡是空指針解除引用”。在街道的一個拐角,我看到一些程序員不知羞恥的 拷貝、粘貼著代碼。
我看凱利正開著一輛出租車。我示意他停下了。
“ 帶我離開這裡,凱利,”我邊爬進車裡邊喊道。“帶我回辦公室去,在那裡我可 以寫出理智的代碼。”
“凱利?我叫奧尼爾。我不知道什麼代碼;我只是 個的哥。”我們驅車時碰到了溫迪,她身穿一身刑警制服。(譯者:估計是code inspection 的主力吧:)凱利向她揮手,想引起她注意。我知道這只是場夢,我不會就當沒 有看見他在干什麼,忽略他。
我們在破舊的辦公室大樓前停了下來。我大跳出來並鑽 進了裡邊。我發現了一塊白板,那些惡劣的代碼就寫在那上邊,我把它重寫了:
{ // ... 一些塊域 ...
T tmp;
U u( tmp );
}
我一把最後一個標記 寫完時,突然醒了過來。 “我的天!”我自言自語著,我跑到了餐廳,弄來了一 杯可以濃濃的咖啡。
注釋 [1] ISO/IEC 14882:1998(E), "國際標准,編程語 言――C++",條款 5.2.3, 8.5.3, 3.10。
關於作者 Jim Hyslop 是Leitch Technology International 公司的一名高級軟件設計師,可以通過電子郵件 [email protected] 和他取得聯系。