1. 程式人生 > >C++建構函式中丟擲的異常

C++建構函式中丟擲的異常

建構函式中丟擲的異常

  1、標準C++中定義建構函式是一個物件構建自己,分配所需資源的地 方,一旦建構函式執行完畢,則表明這個物件已經誕生了,有自己的行為和內部的執行狀態,之後還有物件的消亡過程(解構函式的執行)。可誰能保證物件的構造 過程一定能成功呢?說不定系統當前的某個資源不夠,導致物件不能完全構建好自己(人都有畸形兒,更何況別的呢?朋友們!是吧!),因此通過什麼方法來表明 物件的構造失敗了呢?C++程式設計師朋友們知道,C++中的建構函式是沒有返回值的,所以不少關於C++程式設計方面的書上得出結論:“因為建構函式沒有返回 值,所以通知物件的構造失敗的唯一方法那就是在建構函式中丟擲異常”。主人公阿愚非常不同意這種說法,誰說的,便不信邪!雖然C++標準規定建構函式是沒 有返回值,可我們知道每個函式實際上都會有一個返回值的,這個值被儲存在eax暫存器中,因此實際上是有辦法通過程式設計來實現建構函式返回一個值給上層的對 象建立者。當然即便是建構函式真的不能有返回值,我們也可以通過一個指標型別或引用型別的出參來獲知物件的構造過程的狀態。示例如下:

class MyTest_Base
{
public:
MyTest_Base (int& status)
{
//do other job

// 由於資源不夠,物件構建失敗
// 把status置0,通知物件的構建者
status = 0;
}

protected:
};

void main()
{
int status;
MyTest_Base obj1(status);

// 檢查物件的構建是否成功
if(status ==0) cout << “物件構建失敗” << endl;
}

  程式執行的結果是:
  物件構建失敗
  是啊!上面我們不也得到了物件構造的成功與否的資訊了嗎?可大家有沒有覺得這當中有點問題?主人公阿愚建議大家在此停留片刻,仔細想想它會有什麼問題?OK!也許大家都知道了問題的所在,來驗證一下吧!

class MyTest_Base
{
public:
MyTest_Base (int& status)
{
//do other job

// 由於資源不夠,物件構建失敗
// 把status置0,通知物件的構建者
status = 0;
}

virtual ~ MyTest_Base ()
{
cout << “銷燬一個MyTest_Base型別的物件” << endl;
}


protected:
};

void main()
{
int status;
MyTest_Base obj1(status);

// 檢查物件的構建是否成功
if(status ==0) cout << “物件構建失敗” << endl;
}

  程式執行的結果是:
  物件構建失敗
  銷燬一個MyTest_Base型別的物件

  沒錯,物件的解構函式被運行了,這與C++標準中所規定的面向物件的一些特性是有衝突的。一個物件都沒有完成自己的構造,又何來析構!好比一個 夭折的畸形兒還沒有出生,又何來死之言。因此這種方法是行不通的。那怎麼辦?那就是上面那個結論中的後一句話是對的,通知物件的構造失敗的唯一方法那就是 在建構函式中丟擲異常,但原因卻不是由於建構函式沒有返回值而造成的。恰恰相反,C++標準中規定建構函式沒有返回值正是由於擔心很容易與面向物件的一些 特性相沖突,因此乾脆來個規定,建構函式不能有返回值(主人公阿愚的個人理解,有不同意見的朋友歡迎討論)。

  2、建構函式中丟擲異常將導致物件的解構函式不被執行。哈哈^-^,阿愚很開心,瞧瞧!如果沒有C++的異常處理機制鼎立支援,C++中的面向 物件特性都無法真正實現起來,C++標準總不能規定所有的物件都必須成功構造吧!這也太理想化了,也許只有等到共產主義社會實現的那一天(CPU可以隨便 拿,記憶體可以隨便拿,所有的資源都是你的!)才說不定有可能·····,所以說C++的異常處理和麵向物件確實是誰也離不開誰。當然示例還是要看一下,如 下:

class MyTest_Base
{
public:
MyTest_Base (string name = “”) : m_name(name)
{
throw std::exception(“在建構函式中丟擲一個異常,測試!”);
cout << “構造一個MyTest_Base型別的物件,物件名為:”<<m_name << endl;
}

virtual ~ MyTest_Base ()
{
cout << “銷燬一個MyTest_Base型別的物件,物件名為:”<<m_name << endl;
}

void Func() throw()
{
throw std::exception(“故意丟擲一個異常,測試!”);
}
void Other() {}

protected:
string m_name;
};

void main()
{
try
{
// 物件構造時將會丟擲異常
MyTest_Base obj1(“obj1”);

obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}

  程式的執行結果將會驗證:“建構函式中丟擲異常將導致物件的解構函式不被執行”

  3、是不是到此,關於建構函式中丟擲異常的處理的有關討論就能結束了呢?非也!非也!主人公阿愚還有進一步的故事需要講述!來看一個更復雜一點的例子吧!如下:

class MyTest_Base
{
public:
MyTest_Base (string name = "") : m_name(name)
{
cout << "構造一個MyTest_Base型別的物件,物件名為:"<<m_name << endl;
}

virtual ~ MyTest_Base ()
{
cout << "銷燬一個MyTest_Base型別的物件,物件名為:"<<m_name << endl;
}

void Func() throw()
{
throw std::exception("故意丟擲一個異常,測試!");
}
void Other() {}

protected:
string m_name;
};

class MyTest_Parts
{
public:
MyTest_Parts ()
{
cout << "構造一個MyTest_Parts型別的物件" << endl;
}

virtual ~ MyTest_Parts ()
{
cout << "銷燬一個MyTest_Parts型別的物件"<< endl;
}
};

class MyTest_Derive : public MyTest_Base
{
public:
MyTest_Derive (string name = "") : m_component(), MyTest_Base(name)
{
throw std::exception("在MyTest_Derive物件的建構函式中丟擲了一個異常!");

cout << "構造一個MyTest_Derive型別的物件,物件名為:"<<m_name << endl;
}

virtual ~ MyTest_Derive ()
{
cout << "銷燬一個MyTest_Derive型別的物件,物件名為:"<<m_name << endl;
}

protected:
MyTest_Parts m_component;
};

void main()
{
try
{
// 物件構造時將會丟擲異常
MyTest_Derive obj1("obj1");

obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}

  程式執行的結果是:
  構造一個MyTest_Base型別的物件,物件名為:obj1
  構造一個MyTest_Parts型別的物件
  銷燬一個MyTest_Parts型別的物件
  銷燬一個MyTest_Base型別的物件,物件名為:obj1
  在MyTest_Derive物件的建構函式中丟擲了一個異常!

  上面這個例子中,MyTest_Derive從MyTest_Base繼承,同時MyTest_Derive還有一個MyTest_Parts 型別的成員變數。現在MyTest_Derive構造的時候,是在父類MyTest_Base已構造完畢和MyTest_Parts型別的成員變數 m_component也已構造完畢之後,再丟擲了一個異常,這種情況稱為物件的部分構造。是的,這種情況很常見,物件總是由不斷的繼承或不斷的聚合而 來,物件的構造過程實際上是這些所有的子物件按規定順序的構造過程,其中這些過程中的任何一個子物件在構造時發生異常,物件都不能說自己完成了全部的構造 過程,因此這裡就有一個棘手的問題,當發生物件的部分構造時,物件將析構嗎?如果時,又將如何析構呢?

  從執行結果可以得出如下結論:
  (1) 物件的部分構造是很常見的,異常的發生點也完全是隨機的,程式設計師要謹慎處理這種情況;
   (2) 當物件發生部分構造時,已經構造完畢的子物件將會逆序地被析構(即異常發生點前面的物件);而還沒有開始構建的子物件將不會被構造了(即異常發生點後面的 物件),當然它也就沒有析構過程了;還有正在構建的子物件和物件自己本身將停止繼續構建(即出現異常的物件),並且它的析構是不會被執行的。

  建構函式中丟擲異常時概括性總結
  (1) C++中通知物件構造失敗的唯一方法那就是在建構函式中丟擲異常;
  (2) 建構函式中丟擲異常將導致物件的解構函式不被執行;
  (3) 當物件發生部分構造時,已經構造完畢的子物件將會逆序地被析構;
  (4) 哈哈^-^,其是還是那句話, “C++的異常處理不會破壞任何一條面向物件的特性!”,因此主人公阿愚再次建議朋友們,牢牢記住這一條!

相關推薦

C++建構函式異常

建構函式中丟擲異常會有怎樣的影響呢?如下實驗程式碼 #include <iostream> #include <stdexcept> using namespace std; class A { public: A(int n){} ~A(){cout

C++建構函式異常

建構函式中丟擲的異常   1、標準C++中定義建構函式是一個物件構建自己,分配所需資源的地 方,一旦建構函式執行完畢,則表明這個物件已經誕生了,有自己的行為和內部的執行狀態,之後還有物件的消亡過程(解構函式的執行)。可誰能保證物件的構造 過程一定能成功呢?說不定系統當前的某

27.能否在建構函式異常?解構函式呢?

首先,我們要明確一點!一個函式執行的過程中,如果丟擲異常,會導致函式提前終止! 在C++建構函式中,既需要分配記憶體,又需要丟擲異常時要特別注意防止記憶體洩露的情況發生。因為在建構函式中丟擲異常,在概念上將被視為該物件沒有被成功構造,因此當前物件的解構函式就不會被呼叫。同時

不要在解構函式異常

轉載 : http://www.cnblogs.com/hbt19860104/archive/2012/10/22/2734006.html (很好的博文,贊!!!。解惑瞭如何處理析構函數出現異常現象,增加對解構函式的工作機制和作用域的相關理解。) 不要在解構函式中丟擲異常 1: 可以

C++建構函式和解構函式可以異常嗎?

一.建構函式 1. 建構函式中丟擲異常,會導致解構函式不能被呼叫,但物件本身已申請到的記憶體資源會被系統釋放(已申請到資源的內部成員變數會被系統依次逆序呼叫其解構函式)。 2. 因為解構函式不能被呼叫,所以可能會造成記憶體洩露或系統資源未被釋放。 3. 建構函式中可以丟擲異

Item 8:解構函式不要異常 Effective C++筆記

Item 8: Prevent exceptions from leaving destructors. 解構函式不要丟擲異常 由於解構函式常常被自動呼叫,在解構函式中丟擲的異常往往會難以捕獲,引發程式非正常退出或未定義行為。 例如,物件陣列被析構時,會丟擲多於一個的

c#建構函式的this和base

首先要明確: this指向的本例項的建構函式,base指向的時基類的建構函式。 再執行順序上,a、this表明,在執行本建構函式之前,先執行this指向本例項的建構函式,再執行本函式。          

自定義的異常異常處理異常

你可以用raise語句來引發一個異常。異常/錯誤物件必須有一個名字,且它們應是Error或Exception類的子類 下面是一個引發異常的例子: class ShortInputException(Exception): ‘’‘自定義的異常類’’’ def init(self, le

C++建構函式呼叫虛擬函式是否有多型的效果

C++多型的一個重要應用就是虛擬函式。但是當我們再基類的建構函式中呼叫一個子類過載的虛擬函式會出現多型的效果嗎?我們具體看一下下面的例項: #include <iostream> #define P(x) std::cout<<x<<std::endl;

Fork/Join框架(五)在任務異常

宣告:本文是《 Java 7 Concurrency Cookbook 》的第五章,作者: Javier Fernández González     譯者:許巧輝 校對:方騰飛 在任務中丟擲異常 在Java中有兩種異常: 已檢查異常(Checked exceptions):這些異常必須在一

C++建構函式呼叫虛擬函式

談談關於建構函式中呼叫虛擬函式的情況,僅討論單繼承,不考慮虛擬繼承和多重繼承。 測試平臺:VS2013 + Win7X64 一個例子: #include <stdlib.h> #i

spring事務控制的方法異常不回滾

最近做專案時,由於業務需要,在service中丟擲自定義異常時出現了事務不回滾的情況,具體情況如下: public void editEpidemic(Epidemic epidemic) throws EpidemicException{

c++建構函式初始化列表的作用和機制

#include <iostream> #include <string> using namespace std; class base { private: int m_i; int m_j; public: base(int i): m_j(i), m_i(m_j){}

C++ 建構函式呼叫虛擬函式

我們知道:C++中的多型使得可以根據物件的真實型別(動態型別)呼叫不同的虛擬函式。這種呼叫都是物件已經構建完成的情況。那如果在建構函式中呼叫虛擬函式,會怎麼樣呢? 有這麼一段程式碼: class A { public: A ():m_iVal(0){test();}

c++ 建構函式explicit關鍵字的作用

C++ explicit關鍵字的作用主要是用來修飾類的建構函式,表明該建構函式是顯式的,禁止單引數建構函式的隱式轉換。 如果C++類的建構函式有一個引數,那麼在編譯的時候就會有一個預設的轉換操作:將該建構函式對應資料型別的資料轉換為該類物件,如下面所示:class MyCl

C++建構函式不呼叫虛擬函式的原因

今天在看網上一篇帖子的時候看到這個問題,試講關於C++物件虛擬函式表和型別資訊的(RTTI)的,正好看到了如下內容: 這個是為什麼在建構函式中不能呼叫虛擬函式的原因 .因為任何時候在基類中的虛擬函式呼叫,都不可能到達子類的實現,因為子類的虛表初始化是在呼叫完基類的構造之後發

如何在Service方法異常並返回狀態或校驗結果?

在Service方法中,如果我們通過丟擲異常來進行事務處理(其實有更好的辦法:Spring事務配置),那麼我們的Service方法需要丟擲異常,但這裡有一個問題,之前都沒有考慮到,今天測試介面的時候發現的,問題:當發現檢驗失敗的時候,我們給Map設定了一個key為messa

[c/c++]建構函式、解構函式可不可以異常

usingnamespace std;class A...{public:    A()    ...{        cout <<"construction fun"<< endl;        throw1;    }    ~A()    

C++建構函式和解構函式異常問題

一. 丟擲異常 1.1 丟擲異常(也稱為拋棄異常)即檢測是否產生異常,在C++中,其採用throw語句來實現,如果檢測到產生異常,則丟擲異常。 該語句的格式為: throw 表示式; 如果在try語句塊的程式段中(包括在其中呼叫的函式)發現了異常,且拋棄了

C#如果用await關鍵字來await一個為null的Task物件會異常

await & async模式是C#中一個很重要的特性,可以用來提高非同步程式(多執行緒程式)的執行效率。但是如果嘗試用await關鍵字來await一個為null的Task物件,會導致程式丟擲NullReferenceException異常。   新建一個.NET Core控制檯專案,貼