【原創】Performanced C++ 經驗規則 第三條:你不知道的建構函式(下)

來源:互聯網
上載者:User

第三條:你不知道的建構函式(下)

前面兩篇,我們已經討論了C++建構函式中諸多細枝末節,但百密一疏,還有一些地方我們沒有考慮到。這一篇將對這些問題進行完結。

7、建構函式中的異常

當你在建構函式中寫代碼的時候,你有沒有想過,如果建構函式中出現異常(別告訴我,你不拋異常。“必要”時系統會替你拋的),那會出現怎樣的情況?

對象還能構建完成嗎?建構函式中已經執行的代碼產生的負面效應(如動態分配記憶體)如何解決?對象退出其範圍時,其解構函式能被調用嗎?

上述這些問題,正是建構函式中產生異常要面臨的問題。讓我們先看結論,再分析過程:儘可能不要在建構函式中產生(拋出)異常,否則,一定會產生問題

我們先看一段代碼:

 1 #include <iostream> 2 #include <exception> 3 #include <stdexcept> 4 using namespace  std; 5  6  7 class ConWithException 8 { 9 public:10     ConWithException() : _pBuf(NULL)11     {12         _pBuf = new int[100];13         throw std::runtime_error("Exception in Constructor!");14     }15 16     ~ConWithException()17     {18         cout << "Destructor!" << endl;19         if( _pBuf != NULL )20         {21             cout <<  "Delete buffer..." << endl;;22             delete[] _pBuf;23             _pBuf = NULL;24         }25     }26 27 private:28     int* _pBuf;29 };30 31 int main(int argc, char** argv)32 {33     ConWithException* cwe = NULL;34     try35     {36         cwe = new ConWithException;37     }38     catch( std::runtime_error& e )39     {40         cout<< e.what() << endl;41     }42 43     delete cwe;44 45     return 0;46 }47                                                         

這段代碼運行結果是什麼呢?

輸出

1 Exception in Constructor!

輸出“Exception in Constructor!"說明,我們拋出的異常已經成功被捕獲,但有沒有發現什麼問題呢?有一個很致命的問題,那就是,對象的解構函式沒有被調用!也就是說,delete cwe這一句代碼沒有起任何作用,相當於對delete NULL指標。再往上推,我們知道cwe值還是初始化的NULL,說明對象沒有成功的構建出來,因為在建構函式中拋出了異常,終止了建構函式的正確執行,沒有返回對象。即使我們把cwe = new ConWithException換成在棧中分配(ConWithException cwe;),仍是相同的結果,但cwe退出其範圍時,其解構函式也不會被調用,因為cwe根本不是一個正確的對象!繼續看,在這個建構函式中,為成員指標_pBuf動態申請了記憶體,並計劃在解構函式中釋放這一塊記憶體。然而,由於建構函式拋出異常,沒有返回對象,解構函式也沒有被調用,_pBuf指向的記憶體就發生了泄露!每調用一次這個建構函式,就泄露一塊記憶體,產生嚴重的問題。現在,你知道了,為什麼不能在建構函式中拋出異常,即使沒有_pBuf這樣需要動態申請記憶體的指標成員存在。

然而很多時候,異常並不是由你主動拋出的,也就是說,將上述建構函式改造成這樣:

   ConWithException() : _pBuf(NULL)   {        _pBuf = new int[100];   }

這是我們十分熟悉的格式吧?沒錯,但是,這樣的寫法仍然可能產生異常,因為這取決於編譯器的實現。當動態記憶體分配失敗時,編譯器可能返回一個NULL指標(這也是慣用方式),OK,那沒有問題。但是,有些編譯器也有可能引發bad_alloc異常,如果對異常進行捕獲(通常也不會這樣做),結果將同上述例子所示。而如果未對異常進行捕獲,結果更加糟糕,這將產生Uncaught exception,通常將導致程式終止。並且,此類問題是運行階段可能出現的問題,這將更難發現和處理。

說了半天,就是認為上述寫法,還不夠好,不OK,接下來講述解決方案。

解決方案一:使用智能指標shared_ptr(c++0x後STL提供,c++0x以前可採用boost),注意,在此處不能使用auto_ptr(因為要申請100個int,而即使申請的是單個對象,也不建議使用auto_ptr,關於智能指標,本系列後面的規則會有講述);

解決方案二:就是前面多次提到的,採用"原廠模式"替換公有建構函式,從而儘可能使建構函式“輕量級“

class ConWithException //為和前面比對,類名沒改,糟糕的類名{public:    ConWithException* factory(some parameter...)    {        ConWithException* cwe = new ConWithException;        if(cwe)        {            cwe->_pBuf = new int[100];            //other initialization...        }        return cwe;    }        ~ConWithException()    {        if(cwe->_pBuf)        {            delete[] cwe->_pBuf;            _pBuf = NULL;        }        //other destory process...    }private:    ConWithException() : _pBuf(NULL) {} //如果有非靜態const成員還需要在初始化列表中進行初始化,否則什麼也不做    int* _pBuf;};

使用“原廠模式”的好處是顯而易見的,上述建構函式中異常的問題可以得到完美解決?why?因為建構函式十分輕量級,可輕鬆的完成對象的構建,“重量級”的工作都交由“工廠”(factory)方法完成,這是一個公有的普通成員函數,如果在這個函數中產生任何異常,因為對象已經正確構建,可以完美的進行異常處理,也能保證對象的解構函式被正確地調用,杜絕memory leak。建構函式被聲明為私人,以保證從工廠“安全”地產生對象,使用“原廠模式”,還可以禁止從棧上指派至(其實Java、Objective-C都是這麼做的),在必要的時候,這會很有協助。

8、建構函式不能被繼承:雖然子類對象中包含了基類對象,但並不能代表建構函式被繼承,即,除了在子類建構函式的初始化列表裡,你可以顯式地調用基類的建構函式,在子類的其它地方調用父類的建構函式都是非法的。

9、當類中有需要動態分配記憶體的成員指標時,需要使用“深拷貝“重寫拷貝建構函式和賦值操作符,杜絕編譯器“用心良苦”的產生自動產生版本,以防資源申請、釋放不正確。

10、除非必要,否則最好在建構函式前添加explicit關鍵字,杜絕隱式使建構函式用作自動類型轉換。

終於寫完了,這三篇有關建構函式的“經驗”之談,其實,這些問題,也是老生常談了。經過這三篇的學習,為敲開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.