小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

可爆炸性析構函數(shù)

 quasiceo 2012-11-30

可爆炸性析構函數(shù)

可爆炸性析構函數(shù)

日期:2004/02/28– 2007/01/07

本文首次刊登于《游戲創(chuàng)造》,現(xiàn)開放與大家共享,轉載請注明出處。
下載地址

作者介紹

            唐亮(千里馬肝),四年游戲從業(yè)經驗,曾任職于大宇軟星科技(上海)有限公司任程序技術指導,現(xiàn)在ATI任職Engineer,主要負責ATI CrossFireXP/Vista上的開發(fā)和維護。迄今為止主要個人作品為《阿貓阿狗2》,參與開發(fā)《漢朝與羅馬》、《阿貓阿狗大作戰(zhàn)OLG》和《仙劍奇?zhèn)b傳4》,主要研究方向為C++、圖形渲染技術和系統(tǒng)架構。

blog地址:http://oiramario.cnblogs.com

 

 

開場白

本文是鄙人在2004年有感而發(fā)寫下的一篇文章,當時受到他人討論的啟發(fā),不由得興從心頭起,code從手中生。文中所介紹的技法,可能以今天的眼光來看,尚有不足之處,不過它提出了一種比較新奇的方法來解決問題,我想其思想本身才是需要注意的重點。C++是一個靈活自如的語言,身為C++飯,吾輩有責任將她發(fā)揚光大,我相信還有更多有趣的技巧,有待大家一起挖掘,希望本文能起到拋磚引玉的作用,謝謝。

 

前言

任何語言,任何程序,都會有“操作失敗”的情況發(fā)生。在C語言這種結構化編程語言中,處理這種情況的方法就是通過返回值來表現(xiàn)。在我們的游戲中,往往會因為這樣那樣的不小心,存在著成百上千的bug,有時候真是“修不完理還亂”,甚至有的游戲在不得已的情況下,還將bug遺留到發(fā)行后再通過patch的方式來進行彌補??梢砸姷?,在沒有一個好的方法來避免bug產生的時候,我們面對bug是多么得無奈。我們應該如何避免它,以及如何通過一個好的方法來捕捉它呢?

這實在是一個全方位的問題,例如前期進行仔細的項目分析,中期保持清晰的邏輯從而編寫強壯的代碼,后期通過詳盡的log信息來回溯事故現(xiàn)場,對大量異常條件的處理,使用大量的Assert對不正確參數(shù)的斷言。所有的這些方法,都是想在調試期就將問題盡量全部得排除,把bug們扼殺在搖籃里。

但是這畢竟是理想的情況,人非圣賢孰能無過?誰也不能保證自己永遠處于邏輯亢奮狀態(tài),而純理論往往討論的是一種“烏托邦”的理想國度,但是我們不是學院派,期望能出現(xiàn)一種切合實際的解決方案。所以,當意識到人的惰性的必然性,那么就需要產生出一種制度來進行控管和規(guī)避。那么接下來,我將會介紹了一種新的方法,它叫作:可爆炸性析構函數(shù)。

 

現(xiàn)場

通常我們會將某函數(shù)設計成:在操作成功的情況下返回0;當遇到錯誤發(fā)生時,可能會用1代表內存分配失敗,用2代表文件打開失敗,用3代表無法找到設備等等。雖然這樣看起來很美,但是實際執(zhí)行下來,通常會出現(xiàn)以下的幾個問題:

 

1.       要求同步維護文檔,說明各個返回值所代表的意義。

2.       在某些情況下可能需要修改其返回值所代表的意義,例如將返回值1從內存分配失敗改成代表成功,這樣一來,函數(shù)使用者的代碼就需要修改。

3.       最重要的是,使用者完全可以不檢查返回值。這樣一來,如果接下來的代碼依賴于該函數(shù)必須正確執(zhí)行完成(通常我們都這樣假設),一旦發(fā)生錯誤,按照順序執(zhí)行的流程,下面的代碼照樣會執(zhí)行,從而一錯再錯,變成破罐子破摔。以我多年Debug的經驗,通常最不好修的bug都是由此而引起的。

 

所以,在面向對象的語言中,如DELPHIC++,都引入了“異?!边@種概念來處理錯誤。當出現(xiàn)錯誤時,實現(xiàn)者可以選擇拋出異常,這意味著如果調用者不對該異常進行處理,該異常將會按照函數(shù)調用堆棧一級級地向上拋出,直到找到對應的處理模塊,如果一直拋到最外層的main函數(shù)都無法找到,則程序會立即中止。

但是以C++為例,為了處理異常,C++需要維護像是函數(shù)調用堆棧等一類的東西,這樣會對程序的執(zhí)行效率和空間上帶來開銷。對比異常所帶來的好處,一般的程序大都可以忽略這種開銷;但是像是一些對效率和內存空間要求很高的,如嵌入式或驅動級的程序,通常在這類程序的Coding Standard里就直接被聲明為不被允許使用。所以,這時又不得不退回來重新使用返回值來處理錯誤。

那么我們應該怎么辦呢?考慮到使用者完全可以忽略返回值的問題,于是就有了接下來的方法。

 

分析

            我們的口號是:強迫使用者必須檢查函數(shù)的返回值,如果返回值不被檢查的話,將會在運行期彈出錯誤以警告使用者。那么首先概略得設計一下,就是將會有一個bool變量 ,暫且稱作為checked,將會在返回值構造時初始為false,只有返回值被使用者檢查了,才會被置為true,然后在返回值析構時會判斷checked變量是否為true,否則將立即報錯。

            當然,實現(xiàn)方法是將這種概念用“類”來表現(xiàn),使用者在使用該類(以下統(tǒng)一稱作類型T)作為函數(shù)返回值時,假設原本是以int作為返回值,則該類所表現(xiàn)的行為和操作,應該與int“完全一致”。

            而“檢查”的概念,我認為在語言表達中,即是:

            T::operator == ()

            T::operator != ()

            T::operator int ()

            為了保證“傳遞性”,當T的實例x作為返回值返回時,有以下二條應該被遵守:

1.       如果x不被檢查,則x會在析構時報錯

2.       y=x時,x會被認為已經將“責任”傳遞給了y,x解除責任,而y則有義務同上

 

整理

1.       因為T將用來代替int,則T應有operator int()

2.       為了與int的行為保持一致性,T應該重載operator ==operator !=

3.       為了支持所有返回值的類型,所以T被實現(xiàn)為一個template

4.       為了只在DEBUG期進行,避免RELEASE期的開銷,則有

enum ErrType
{
           Success,
           Fail
};

#ifdef CHECK_RESULT
           typedef InspectResult<>                          ResultInt;
           typedef InspectResult<ErrType>  ResultEnum;
#else
           typedef int                     ResultInt;
           typedef ErrType ResultEnum;
#endif

ResultInt Func1()
{
           ResultInt ret = 1;
           return ret;
}

ResultEnum Func2()
{
           ResultEnum ret = Success;
           return ret;
}

5.       如果返回值是一個“大的類型”如string,為避免臨時變量產生導致開銷,以及不同的調用方式和習慣的支持,則有const string &result()const

 

實現(xiàn)

template <typename ResultType=int>

class InspectResult

{

            mutable bool      _checked;          // 檢查標志

            ResultType                     _ret;                  // 返回值

 

public:

            /*-------------------------------------------------------------

                        構造函數(shù)

            -------------------------------------------------------------*/

            InspectResult(const ResultType &ret)

            : _checked(false), _ret(ret)

            {

            }

 

            /*-------------------------------------------------------------

                        拷貝構造函數(shù)

            -------------------------------------------------------------*/

            InspectResult(const InspectResult &rhs)

            : _checked(rhs._checked), _ret(rhs._ret)

            {

                        // rhs"被檢查權"傳遞給this(下同)

                        rhs._checked = true;

            }

 

            /*-------------------------------------------------------------

                        析構函數(shù)

            -------------------------------------------------------------*/

            ~InspectResult()

            {

                        // 如果沒有檢查過返回值則報錯

                        assert(_checked);

            }

 

            /*-------------------------------------------------------------

                        operator =

            -------------------------------------------------------------*/

            InspectResult & operator = (const InspectResult &rhs)

            {

                        _checked = rhs._checked;

                        _ret = rhs._ret;

 

                        rhs._checked = true;

                       

                        return *this;

            }

 

            /*-------------------------------------------------------------

                        重載operator = (const ResultType &ret)

                        以支持InspectResultResultType之間的直接操作

                        因為ctornon-explicit, 以避免臨時變量的產生

            -------------------------------------------------------------*/

            InspectResult & operator = (const ResultType &ret)

            {

                        _checked = false;

                        _ret = ret;

                       

                        return *this;

            }

 

            /*-------------------------------------------------------------

                        所謂"返回值必須檢查", 在此我視為operator ==動作

                        注意這里因為值已被檢查, 所以thisrhs都將視為已檢查

            -------------------------------------------------------------*/

            bool operator == (const InspectResult &rhs)const

            {

                        _checked = rhs._checked = true;

 

                        return _ret == rhs._ret;

            }

 

            /*-------------------------------------------------------------

                        所謂"返回值必須檢查", 在此我視為operator ==動作

                        注意這里因為值已被檢查, 所以_checked = True

            -------------------------------------------------------------*/

            bool operator == (const ResultType &ret)const

            {

                        _checked = true;

 

                        return _ret == ret;

            }

 

            /*-------------------------------------------------------------

                        operator != (const InspectResult &rhs)const

            -------------------------------------------------------------*/

            bool operator != (const InspectResult &rhs)const

            {

                        return !(*this == rhs);

            }

 

            /*-------------------------------------------------------------

                        operator != (const ResultType &ret)const

            -------------------------------------------------------------*/

            bool operator != (const ResultType &ret)const

            {

                        return !(*this == ret);

            }

 

            /*-------------------------------------------------------------

                        operator ResultType

            -------------------------------------------------------------*/

            operator ResultType ()const

            {

                        _checked = true;

 

                        return _ret;

            }

 

            /*-------------------------------------------------------------

                        如果ResultType是一個大的class(string)

                        這時使用operator ResultType會有臨時變量的開銷

                        但若ResultType是內建的類型(char), 則不建議使用本函數(shù)

            -------------------------------------------------------------*/

            const ResultType &result()const

            {

                        _checked = true;

 

                        return _ret;

            }

};

 

 

enum ErrType

{

            Success,

            Fail

};

 

#define CHECK_RESULT

 

#ifdef CHECK_RESULT

            typedef InspectResult<>                          ResultInt;

            typedef InspectResult<ErrType>  ResultEnum;

#else

            typedef int                     ResultInt;

            typedef ErrType ResultEnum;

#endif

 

結論

1.         矯枉不必過正,只需要對于那些“必須成功執(zhí)行”或“必須對執(zhí)行中產生的錯誤進行處理”的函數(shù)使用上面所介紹的方法。

2.         語言是表達思想的一種工具,利用C++的特點(支持運算符的重載)。我們可以實現(xiàn)一些在基本語言層面上無法表現(xiàn)的東西。

3.         通過template,我們可以用泛型實現(xiàn)對所有類型的支持。

4.         因為檢查會有開銷(至少會多出一個bool,內存對齊的情況下類型T會膨脹),通過define,我們可以在需要的時候作檢查,不需要的時候則消除開銷。

5.         為了貫穿思想,實現(xiàn)出來的東西往往不像想的時候那么簡單,需要考慮很多方面。總之,思想是最重要的東西。

 

            :本文及代碼,啟發(fā)自《程序員》20029月中的《C++ Exception》專欄討論,其中myan(孟巖)在與某老外通信中談到此名詞:“可爆炸性析構函數(shù)”,希望能給你帶來啟發(fā)或是幫助。

 

            代碼下載地址:http://oiramario.cnblogs.com/std56.rar

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多