程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ 異常 exception (1)

C++ 異常 exception (1)

編輯:C++入門知識

  // 1:在構造函數中拋出異常,則系統不會執行該對像的析構函數!!!
       // 2:在構造函數中拋出異常,如果對象是在堆中創建的,那會,會delete掉這塊內存.但此時的 delete已經不會再調用析構函數了
       // 3:拋出異常後,異常語句後面的代碼不再執行,但是編譯器會先把類內部成員(已經構造好的對鐘愛)的析構函數調用完後再跳出try()
       // 4:每個異常,必須有相應的try將其包圍,包括 顯式的手工制作的異常.eg throw int(),
也包括 未知的代碼 執行過程中出現的異常,比如MakeXML中出現的異常.eg.string中可能出現的異常
同樣的,每個異常必須有一個catch()將其捕捉
       // :每個catch 必須至少有一個類型.也可是catch(...)這樣的萬能類型
       // :如果catch 沒有捕捉到 try所拋出的對應的異常,出現unhandle exception.

      :當一個異常出現卻沒有被處理.那麼發布後的release程序會.運行後,什麼結果也不顯示,直接關閉,什麼也不提示,!!!!,這種情況發現時,讓你去找bug將會是多麼恐怖!!!
      發布為debug,則會顯示一個 未處理的異常之類的類似於斷言失敗的對話框.
1.     指導方針
    根據讀者們的建議,經過反思,我部分修正在Part14中申明的原則:
l         只要可能,使用那些構造函數不拋異常的基類和成員子對象。
l         不要從你的構造函數中拋出任何異常。
    這次,我將思考讀者的意見,C++先知們的智慧,以及我自己的新的認識和提高。然後將它們轉化為指導方針來闡明和引申那些最初的原則。
    (關鍵字說明:我用“子對象”或“被包容對象”來表示數組中元素、無名的基類、有名的數據成員;用“包容對象”來表示數組、派生類對象或有數據成員的對象。)
 
1.1     C++的精髓
    你可能認為構造函數在遇到錯誤時有職責拋異常以正確地阻止包容對象的構造行為。Herb Sutter在一份私人信件中寫道:
一個對象的生命期始於構造完成。
推論:一個對象當它的構造沒有完成時,它從來就沒存在過。
推論:通報構造失敗的唯一方法是用異常來退出構造函數。
    我估計你正在做這種概念上就錯誤的事(“錯”是因為它不符合C++的精髓),而這也正是做起來困難的原因。
    “C++的精髓”是主要靠口頭傳授的C++神話。它是我們最初的法則,從ISO標准和實際中得出的公理。如果沒有存在過這樣的C++精髓的聖經,混亂將統治世界。Given that no actual canon for the Spirit exists, confusion reigns over what is and is not within the Spirit, even among presumed experts.
    C和C++的精髓之一是“trust the programmer”。如同我寫給Herb的:
最終,我的“完美”觀點是:在錯誤的傳播過程中將異常映射為其它形式應該是系統設計人員選定的。這麼做不總是最佳的,但應該這麼做。C++最強同時也是最弱的地方是你可以偏離你實際上需要的首選方法。還有一些其它被語言許可的危險影行為,取決於你是否知道你正在做什麼。In the end, my "perfect" objective was to map exceptions to some other form of error propagation should a designer choose to do so. Not that it was always best to do so, but that it could be done. One of the simultaneous strengths/weaknesses of C++ is that you can deviate from the preferred path if you really need to. There are other dangerous behaviors the language tolerates, under the assumption you know what you are doing.
    C++標准經常容忍甚至許可潛在的不安全行為,但不是在這個問題上。顯然,認同程序員的判斷力應該服從於一個更高層次的目的(Apparently, the desire to allow programmer discretion yields to a higher purpose)。Herb在C++精髓的第二個表現形式上發現了這個更高層次的目的:一個對象不是一個真正的對象(因此也是不可用的),除非它被完全構造(意味著它的所有要素也都被完全構造了)。
    看一下這個例子:
struct X
   {
   A a;
   B b;
   C c;
   void f();
   };
 
try
   {
   X x;
   x.f();
   }
catch (...)
   {
   }
 
    這裡,A、B和C是其它的類。假設x.a和x.b的構造完成了,而x.c的構造過程中拋了異常。如我們在前面幾部分中看到的,語言規則規定執行這樣的序列:
l         x的構造函數拋了異常
l         x.b的析構函數被調用
l         x.a的析構函數被調用
l         控制權交給異常處理函數
    這個規則符合C++的精髓。因為x.c沒有完成構造,它從未成為一個對象。於是,x也從未成為一個對象,因為它的一個內部成員(x.c)從沒存在過。因為沒有一個對象真的存在過,所以也沒有哪個需要正式地析構。
    現在假設x的構造函數不知怎麼控制住了最初的異常。在這種情況下,執行序列將是:
l         x.f()被調用
l         x.c的析構函數被調用
l         x.b的析構函數被調用
l         x.a的析構函數被調用
l         x的析構函數被調用
l         控制權跳過異常處理函數向下走
於是異常將會允許析構那些從沒被完全構造的對象(x.c和x)。這將造成自相矛盾:一個死亡的對象是從來都沒有產生過的。通過強迫構造函數拋異常,語言構造避免了這種矛盾。
 
1.2     C++的幽靈
    前面表明一個對象當且僅當它的成員被完全構造時才真的存在。但真的一個對象存在等價於被完全構造?尤其x.c的構造失敗“總是”如此惡劣到x必須在真的在被產生前就死亡?
    在C++語言有異常前,x的定義過程必定成功,並且x.f()的調用將被執行。代替拋異常的方法,我們將調用一個狀態檢測函數:
X x;
if (x.is_OK())
   x.f();
或使用一個回傳狀態參數:
bool is_OK;
X x(is_OK);
if (is_OK)
   x.f();
    在那個時候,我們不知何故在如x.c這樣的子對象的構造失敗時沒有強調:這樣的對象從沒真的存在過。那時的設計真的這麼根本錯誤(而我們現在絕不允許的這樣行為了)? C++的精髓真的在那時是不同的?或者我們生活在夢中,沒有想到過x真的沒有成形、沒有存在過?
    公正地說,這個問題有點過份,因為C++語言現在和過去相比已不是同樣的語言。將老的(異常支持以前)的C++當作現在的C++如同將C當作C++。雖然它們有相同的語法,但語意卻是不相同的。看一下:
struct X
   {
   X()
      {
      p = new T; // assume new fails
      }
   void f();
   };
 
X x;
x.f();
 
    假設new語句沒有成功分配一個T對象。異常支持之前的編譯器(或禁止異常的現代編譯器)下,new返回NULL,x的構造函數和x.f()被調用。但在異常允許後,new拋異常,x構造失敗,x.f()沒有被調用。同樣的代碼,非常不同的含意。
    在過去,對象沒有自毀的能力,它們必須構造,並且依賴我們來發現它的狀態。它們不處理構造失敗的子對象。並且,它們不調用標准運行庫中拋異常的庫函數。簡而言之,過去的程序和現在的程序存在於不同的世界中。我們不能期望它們對同樣的錯誤總有同樣的反應。
 
1.3     這是你的最終答案嗎?
    我現在相信C++標准的行為是正確的:構造函數拋異常將析構正在處理的對象及其包容對象。我不知道C++標准委員會制訂這個行為的精確原因,但我猜想是:
l         部分構造的對象將導致一些微妙的錯誤,因為它的使用者對其的構造程度的假設超過了實際。同樣的類的不同對象將會有出乎意料的和不可預測的不同行為。
l         編譯器需要額外的紀錄。當一個部分構造的對象消失時,編譯器要避免對它及它的部分構造的子對象調用析構函數。
l         對象被構造和對象存在的等價關系將被打破,破壞了C++的精髓。
 
1.4     對對象的使用者的指導
    異常是對象的接口的一部分。如果能夠,事先准備好接口可能拋的異常集。如果一個接口沒有提供異常規格申明,而且又不能從其它地方得知

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved