C++陰暗面

來源:互聯網
上載者:User

近來一篇<The Dark Side Of C++>在坊間廣為轉載,作為一個以C++為吃飯傢伙的程式員,還是應該下載下來好好讀一讀的。 總的來講還是總結的蠻全的,由於個人知識的限制,我讀完後將其分為三類:一類是我不以為然的,覺得算不上陰暗面;一類是深有同感,深受其害;而另外一類則是還不理解,需要日後有時間的時候加以研究的。

一、不以為然
  • 不斷變更的標準,迫使我們需要不斷更新已有代碼。
    作者列出了幾點其實影響並不是很大(迴圈變數的scope;標頭檔尾碼;名字空間)。而且,為了標準的進步,偶爾做出的妥協也是應該的吧。
  • 不斷變更的style,作者舉得例子是:Old and busted:
    for (int i = 0; i < n; i++)
    New hotness:
    for (int i(0); i != n; ++i)

      有這個問題嗎?有人用第二種方式的嗎? 

  • auto_ptr很爛。
    這個,你不用它就是了,而且最近在智能指標加入C++0x大家庭後,應該達成共識了吧
  • iterator可能會失效。
    這個我感覺還好,沒遇到過太多問題;
  • iterator對container毫不知情。
    我覺得這是STL設計的一個優點吧,通過iterator解耦演算法與容器。
  • vector::at會做邊界檢查,而operator []不會。
    很好啊,提供兩種選擇
  • 建構函式與解構函式中的虛函數調用,可能會調用基類的虛函數,甚至是純虛函數。
    這個不怎麼陰暗吧,不要在建構函式與解構函式總調用虛函數應該是個常識,而且,其他語言難道沒有這個問題?
二、深有同感
  • 模板中排山倒海式的編譯錯誤,在繁瑣無比的錯誤後面,原因可能僅僅是用錯了iterator類型。那個被斃掉的C++ 0x proposal不知是否可以解決此問題。
  • 用C++寫的代碼不太容易讀,函數重載,操作符重載,虛函數重定義,類型重定義,宏定義等等,把代碼的真實面貌嚴嚴實實的藏在了身後。(當然,這也是為了抽象與一致性),幾個例子:string a("blah"); // 定義一個string對象
    string a(); //聲明一個函數

    a && b // 如果&&沒被重定義,是短路計算;但若是被重載了,那麼可能兩個都要計算

    typedef OtherType& Type;
    Type a = b;
    a.value = 23; // 不看到那個typedef,鬼知道b的值會不會被改掉

     另外, baz = foo->bar(3);如此簡單的一行代碼背後蘊含的無窮可能,也充分體現了C++代碼難讀的特點。

  • 關於cin為什麼typecast到void*,而不是bool的討論,凸顯了C++中劍走偏鋒的情況 - 火候不到,一招不慎就很容易傷及自身。
  • 解構函式中不應該拋出異常,我以前只知道一個原因 - 就是在前次異常的棧展開過程中調用解構函式並拋出異常,會導致程式退出。但這裡給出了我覺得更令人信服的原因:在delete []數組的時候,前面對象析構拋出異常,會導致數組中其他對象記憶體泄露。
  • 類成員的初始化順序由其定義的順序決定,而不是初始化列表中的順序 - 這點的確引起了較大的迷惑,也帶來了不少bug - 因為C++的行為是反直覺的。
  • 函數調用中,傳指標的方式比較明顯的告訴你該函數可能會改變這個參數,而引用卻沒這麼明顯,文法和傳值調用一樣,卻也可以改變參數值。
  • C++過於強大,過於靈活,很多人無法很好的掌控 - 太多複雜的feature set,要用好它,你可以讀個博士了~~~
  • prefix ++的重載文法是:operator++(yourtype&), 為了加以區別,postfix的重載文法有個dummy的int參數:operator++(yourtype&, int dummy)。
    雖然我也沒有更好的方法,但我承認這的確很傻。
  • 同樣的容器,由於使用了不同的allocator就無法互動了,這可以理解,因為STL中allocator是容器類型的一部分,allocator不同導致容器類型不同 - 但這不得不讓我們思考STL用這種方式提供allocator是不是合適。
  • map的operator[]自動添加元素,如果不存在的話。
    因為相比於find和insert,operator []實在是太方便了,這個方便的誘惑的確造成了不少麻煩。
  • 模板中你不得不把>>寫成> >。因為>>已經被佔用了。
  • 用不用exception,如何用好exception實在是個太大太深的話題,都可以在大學開個博士學位了。其中異常安全中resource leak,deadlock是常見的問題。
  • delete []可以很好的處理退化為指標的數組,如果是類的話調用會調用的解構函式s,因為數組元素的個數可以通過sizeof(memoryblock)/sizeof(type)求出。
  • new []可能會引起int的溢出,如: new double [0x8000000] = malloc (8 * 0x80000000),超過了int的表達範圍,溢出~
  • 局部靜態變數的初始化不是安全執行緒的 - 這個問題在多線程環境下的單件模式中尤為常見,一般可以用lock解決,但是每次訪問都lock比較費力,所以會用一種double-check lock的方式,但是這種方式由於編譯器最佳化引起的reorder,也會線程不安全,需要使用volatile,或者memory barrier防止最佳化。這個估計可以另外寫篇文章了。
  • 用基類指標操作衍生類別的數組,p++不是指向下一個元素,而是指向了一個不合適的記憶體位址。
  • 如果你在衍生類別中有個函數的名字和基類中的函數名字重複,即使函數原型不一樣,其基類中的函數都將在衍生類別中被隱藏。
    這點的確比較過分!背後有什麼原因呢? 
三、日後研究
  • 關於名字空間,C++有過什麼大的更改嗎?
    這個估計要查查《C++語言的設計與演化》了
  • 用C++寫出好的庫基本是不可能的。
    我看到很多人,包括牛人都說過這個,但是不知有沒有給過一個列表,C++中那些缺點使其寫出好的庫成為不可能,哪些語言可以,為什嗎?
  • 我們不應該在建構函式中拋出異常,因為:Exceptions in constructor don’t unwind the constructor itself。
    這個不太理解,據我所知,在建構函式中拋出異常是建構函式報錯的一個方法,因為建構函式本身不返回任何值。
  • 拋出異常時:Does not even clean up local variables!
    不理解,我們的RAII不就是利用local對象的析構來做記憶體管理的嗎。
  • assert(s[s.size()] == 0); works if s is a const std::string, butis undefined if it is not const
    在VC2008上試了一下,沒問題。為什麼會這麼說,為什嗎? 
  • If you call delete when you should have called delete[], the pointer wilbe off by sizeof(int), leading to heap corruption and possibly code execution.
    不懂。
  • If you call delete[] when you should have called delete, some randomdestructors will be called on garbage data, probably leading to code execution.
    為什麼,delete[]會去計算該數組中有幾個元素,而答案應該是1,那就不該有問題 - 這個可能和上一點的答案有關。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.