C++建構函式中拋出的異常

來源:互聯網
上載者:User

建構函式中拋出的異常

  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++的異常處理不會破壞任何一條物件導向的特性!”,因此主人公阿愚再次建議朋友們,牢牢記住這一條!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.