• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-5  評(píng)論-33  文章-0  trackbacks-0
            推薦使用 boost.property_tree,簡(jiǎn)潔,高效,支持多種格式: ini , xml ...
            并且使用統(tǒng)一的操作接口。
            re: C++的一個(gè)疑惑 luckycat 2010-12-20 22:43
            B(A& a){} 不但是一個(gè)構(gòu)造函數(shù),而且是一個(gè)自定義的類型轉(zhuǎn)換操作( A -> B),你的問(wèn)題有出在這里,如果要去掉這種非有意的自定義類型轉(zhuǎn)換,使用 explicit B(A& a){}。

            一個(gè) 非const引用,只能引用與其類型完全相同的對(duì)象,或者是其派生類的對(duì)象 ,所以 B &refB = objectB ; B &refB = objectB1 都是合法的,但是 B &refB = objectA 就不是合法的
            ,因?yàn)?A 與 B的類型不相同,且不是B的派生類,所以編譯時(shí)會(huì)報(bào)錯(cuò),于是 " B1( B& b ); A a; B1 b1(a)" 就不能通過(guò),簡(jiǎn)化一下就相當(dāng)于 " B &b = a".

            一個(gè) const引用 滿足 非const引用 的特性的同時(shí),還有很重要的一點(diǎn),const 引用可以引用一個(gè)與其類型完全不相同的類型(因?yàn)榫幾g器會(huì)生成一個(gè)轉(zhuǎn)換后可引用的臨時(shí)對(duì)象),
            前提是被引用的類型可以轉(zhuǎn)換為引用的類型(編譯器自定義的類型提升,或者是用戶自定義的類型轉(zhuǎn)換,如上面的 B(A& a)。 ),
            舉個(gè)例子:
            const int &iValue = 3.14; 就是OK的,這里使用編譯器內(nèi)部的類型轉(zhuǎn)換 double -> int.
            const B &b = a; 也是OK的,因?yàn)槭褂?B( A &a) 可以將 A -> B ,于是 const B &b = a; 的背后,編譯器所做的就是:

            const B tempB( a ); //調(diào)用 B( A &a)
            const B &b = tempB;


            BTW: 為什么在const引用情況下,編譯器會(huì)生成一個(gè)可被引用的臨時(shí)對(duì)象,原因很簡(jiǎn)單,你是用一個(gè) const引用 來(lái)操作這個(gè)臨時(shí)對(duì)象,所以,這個(gè)臨時(shí)對(duì)象的狀態(tài)是不會(huì)變的,
            也就是安全的(當(dāng)然,如果你把const引用 const_cast 成一個(gè)非 const引用來(lái)操作這個(gè)編譯器生成的臨時(shí)對(duì)象,那么結(jié)果是未定義的).
            re: Google Test測(cè)試框架 luckycat 2010-05-26 23:00
            @ouyang
            GoogleTest同樣可以用于測(cè)試Win32 GUI Application。
            你的想法可能是MFC寫(xiě)出來(lái)的應(yīng)用是沒(méi)有對(duì)應(yīng)的控制臺(tái),所以GoogleTest無(wú)法將輸出結(jié)果顯示出來(lái)(實(shí)際上Win GUI應(yīng)用程序也可以同時(shí)具備Console Output,只不過(guò)這需要手工編碼實(shí)現(xiàn),默認(rèn)情況下是沒(méi)有的),當(dāng)然GoogleTest已經(jīng)考慮到這個(gè)問(wèn)題了,GoogleTest支持將測(cè)試結(jié)果以XML文件格式輸出.參考下面的鏈接:http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Controlling_Test_Output
            @小蘇
            VC6我這里沒(méi)有,我上面的輸出是在VC2008下的測(cè)試結(jié)果,你換用VC2005/2008再試試.
            我的建議是學(xué)習(xí)C++就不要用VC6了,可以用VC2005/2008.
            如果你想用一個(gè)輕量級(jí)的環(huán)境學(xué)習(xí)C++,MinGW Studio,
            wxDev-Cpp , CodeBlocks , CodeLite 都是不錯(cuò)的選擇.
            @小蘇
            為了更好的理解我上面的分析,你可以打開(kāi)VC2005/VC2008(我這里是VC2008)的單步調(diào)試,
            在調(diào)試模式下的"自動(dòng)窗口"(位于IDE下方).
            觀察memset 前后"man -> strName -> _Bx -> _Ptr"的值的變化
            _Ptr 實(shí)際上就是std::string內(nèi)部用于存儲(chǔ)字符串的堆內(nèi)存緩沖區(qū)的地址,也相當(dāng)于我上面提到的 m_pCharBuffer
            @小蘇
            在閱讀下面的分析之前,希望你對(duì)"C++對(duì)象的內(nèi)部布局"有一定的了解.
            既然你也發(fā)現(xiàn)了內(nèi)存泄漏的情況,那么你再用下面的測(cè)試代碼運(yùn)行一下:



            #include "Windows.h"

            #include <string>
            #include <cstring>
            #include <cstdlib>

            using namespace std;

            typedef unsigned int UINT32;
            typedef unsigned short UINT16;

            typedef struct structMan
            {
            UINT32 sexType; //ENUM_SEXY_TYPE
            UINT16 usAge;
            string strName;
            string strAddress;

            bool operator < (const structMan &man) const
            {
            return usAge < man.usAge;
            }

            bool operator > (const structMan &man) const
            {
            return usAge > man.usAge;
            }
            }MAN;

            int main( int argc , char *argv[] )
            {

            while( true )
            {
            MAN man;
            fprintf( stdout , "before memset: char buffer address( heap address ) to store string = %p , size = %lu , capacity = %lu\n" , \
            *reinterpret_cast< const int* >( man.strName.c_str() ) , \
            man.strName.size() , man.strName.capacity() );
            memset( &man , 0 , sizeof( MAN ) );
            fprintf( stdout , "after memset: char buffer address( heap address ) to store string = %p , size = %lu , capacity = %lu\n\n\n" , \
            *reinterpret_cast< const int* >( man.strName.c_str() ) , \
            man.strName.size() , man.strName.capacity() );

            man.strAddress = "abcdef";
            man.strName = "abc";
            Sleep( 1000 ); //這里sleep是為了讓大家有時(shí)間在任務(wù)管理器中看到內(nèi)存增長(zhǎng)的過(guò)程,不至于一下子耗盡內(nèi)存.
            }


            return 0;
            }


            我選取我這里的一個(gè)循環(huán)中的輸出,如下:


            before memset: char buffer address( heap address ) to store string = 00636200 ,
            size = 0 , capacity = 15
            after memset: char buffer address( heap address ) to store string = 00000000 ,
            size = 0 , capacity = 0

            下面把上述代碼進(jìn)行簡(jiǎn)化便于分析:

            while( true )
            {
            MAN man; //這里會(huì)使用man由編譯器自動(dòng)生成缺省構(gòu)造函數(shù)來(lái)調(diào)用strName的缺省構(gòu)造函數(shù)對(duì)strName進(jìn)行構(gòu)造.

            memset( &man , 0 , sizeof( MAN ) );
            上面的memset操作會(huì)把 &man 這個(gè)地址開(kāi)始的 sizeof( MAN )字節(jié)的內(nèi)存空間全部清零.
            這也就意味著 man 對(duì)象內(nèi)部的每個(gè)成員子對(duì)象所占據(jù)的內(nèi)存都被清零.
            man 對(duì)象內(nèi)部一個(gè) std::string , 而std::string 內(nèi)部包含一個(gè)std::string用于實(shí)際存儲(chǔ)字符串的指向動(dòng)態(tài)分配的堆內(nèi)存的指針,
            我們假設(shè)這個(gè)指針的名稱為 m_pCharBuffer;
            在std::string的析構(gòu)函數(shù)中釋放這個(gè)動(dòng)態(tài)分配的堆內(nèi)存的時(shí)候需要使用這個(gè)m_pCharBuffer,也即是調(diào)用 delete[] m_pCharBuffer;
            如果我寫(xiě)出下在的代碼:
            char *m_pCharBuffer = new char[ BUFFER_SIZE ]; 這一個(gè)操作即是strName的缺少構(gòu)造函數(shù)的操作,只不過(guò) BUFFER_SIZE = 15 + 1(最后有一個(gè)'\0');
            m_pCharBuffer = NULL; //這一個(gè)操作與上述的 memset 對(duì) man.strName中用于指向動(dòng)態(tài)內(nèi)存的指針?biāo)a(chǎn)生的作用相同:將指針?biāo)赶虻亩褍?nèi)存地址清零.
            delete[] m_pCharBuffer; //此時(shí) m_pCharBuffer 為NULL , 不過(guò)在C++中, delete NULL指針是安全的.不過(guò)因?yàn)?m_pCharBuffer 已經(jīng)不指向上述new出來(lái)的內(nèi)存
            //所以這里進(jìn)行 delete[] m_pCharBuffer 時(shí)已經(jīng)不能進(jìn)行資源的釋放了,也即是發(fā)生了內(nèi)存泄漏.

            man.strName = "abc";
            上面的賦值操作中,實(shí)際上要調(diào)用: std::string::operator=( const char* );
            首先,operator=會(huì)判斷當(dāng)前的strName的 capacity能否容納下"abc",由上面的memset之后我們可以看出此時(shí)存儲(chǔ) capacity 值的變量因?yàn)閙emset為0,所以
            man.strName.capacity() 輸出為0,這也就意味著這個(gè)"容積"不能容納下3個(gè)字節(jié)的"abc".

            所以這時(shí) operator= 要擴(kuò)大內(nèi)部用于存儲(chǔ)字符串的緩沖區(qū),擴(kuò)充的基本原理如下:(代碼進(jìn)行簡(jiǎn)化處理)

            std::string& operator=( const char *szString )
            {
            // check parameter

            if( m_pCharBuffer != szString ) // 防止 self-assign
            {
            delete[] m_pCharBuffer;
            m_pCharBuffer = new char[ NEW_SIZE ];
            memcpy( m_pCharBuffer , szString , strlen( szString ) + 1 );
            }

            return *this;

            }

            上面的操作: *reinterpret_cast< const int* >( man.strName.c_str() ) 即是相當(dāng)于獲取這個(gè) m_pCharBuffer 的地址.
            這一點(diǎn)你一定要明白.

            由上面的代碼以及運(yùn)行輸出可以知道,
            注意: 在調(diào)用 strName = "abc"時(shí),已經(jīng)進(jìn)行了memset操作,此時(shí)的 m_pCharBuffer 已經(jīng)因?yàn)樯厦娴?memset操作而被清零,即是 m_pCharBuffer = NULL,
            因?yàn)閙emset操作不會(huì)調(diào)用析構(gòu)函數(shù) ,所以實(shí)際上在清零之前它所指向的動(dòng)態(tài)內(nèi)存塊并沒(méi)有被釋放,
            在 operator=中,delete[] m_pCharBuffer; 相當(dāng)于 delete[] NULL;
            這就不能釋放 m_pCharBuffer 之前在缺省構(gòu)造時(shí)所指向的動(dòng)態(tài)分配的 15 + 1 字節(jié)的內(nèi)存了,所以出現(xiàn)了內(nèi)存泄漏.

            }
            @小蘇
            你運(yùn)行后仔細(xì)觀察這個(gè)編譯后運(yùn)行的exe在"windows任務(wù)管理器"中對(duì)應(yīng)的
            "內(nèi)存使用"數(shù)值.
            我用VC2005和VC2008都測(cè)試過(guò),結(jié)果是"內(nèi)存不停增長(zhǎng)".

            還用哪位同學(xué)運(yùn)行過(guò)我上面的測(cè)試代碼,出來(lái)公布一下測(cè)試結(jié)果,謝謝!
            @陳梓瀚(vczh)
            你列舉的判斷標(biāo)準(zhǔn)都值得借鑒,不過(guò)你后續(xù)補(bǔ)充的對(duì)"例外情況"的處理方式不敢茍同:
            因?yàn)闃?gòu)造可能沒(méi)有成功,那么我們需要調(diào)用IsAvailable之類的函數(shù),甚至于后續(xù)需要因?yàn)榕袛嘀皹?gòu)造函數(shù)的狀態(tài)來(lái)
            對(duì)調(diào)用的每個(gè)成員函數(shù)進(jìn)行"try catch"或者還要從"每個(gè)成員函數(shù)的返回值中來(lái)判斷之前的構(gòu)造操作是否成功".
            這種設(shè)計(jì)是可行的,但對(duì)類的使用者來(lái)說(shuō)太復(fù)雜.
            這種情況下我覺(jué)得使用"兩段構(gòu)造"可能更好一些,我們只需要判斷"兩段構(gòu)造"是否成功即可,如果構(gòu)造成功,在后續(xù)的成員函數(shù)調(diào)用過(guò)程中,
            就再也不用為了確認(rèn)構(gòu)造函數(shù)的狀態(tài)來(lái)對(duì)每個(gè)被調(diào)用的成員函數(shù)進(jìn)行"try catch"或檢查返回值的操作,這樣的設(shè)計(jì)應(yīng)該更簡(jiǎn)潔一些.
            @小蘇
            你后續(xù)修改的代碼,在我看來(lái),即使在多個(gè)編譯器下都是OK的,但是就代碼風(fēng)格來(lái)說(shuō),還有改進(jìn)的地方.
            在編碼過(guò)程中,我很少會(huì)對(duì)struct進(jìn)行memset操作,只是偶爾會(huì)對(duì)sockaddr進(jìn)行memset操作;更不會(huì)對(duì)class進(jìn)行memset操作.

            在你上述的代碼中,你對(duì)MAN進(jìn)行memset操作,無(wú)非也就是想將各個(gè)成員的初值清零,如果基于這個(gè)出發(fā)點(diǎn),那設(shè)計(jì)一個(gè)構(gòu)造函數(shù)多好:
            structMan::structMan( UINT32 enumSexType = SEXY_TYPE_MAN , \
            UINT16 uiAge = 0 , \
            const std::string &refStrName = "" , \
            const std::string &refStrAddress = "" )
            :sexType( enumSexType ) , usAge( uiAge ) , \
            strName( refStrName ) , strAddress( refStrAddress )
            {
            // check parameters here。
            }

            只需要少量的代碼就會(huì)帶來(lái)大量的方便,而且你也就再也不用memset.
            你也就不需要對(duì)struct的各個(gè)成員依次賦值了,直接傳參構(gòu)造就可以了,這樣代碼應(yīng)該會(huì)更優(yōu)雅一些.

            另一方面,對(duì) std::list 進(jìn)行sort操作從邏輯上是沒(méi)有問(wèn)題的,但是設(shè)計(jì)風(fēng)格上是有問(wèn)題的:
            因?yàn)閟td::list中的每一個(gè)成員是基于鏈的形式連接在一起的,所以我們不能對(duì)其進(jìn)行隨機(jī)訪問(wèn),
            如果我們要訪問(wèn)std::list中的第N個(gè)成員,那么我們需要從鏈表頭開(kāi)始向鏈表尾部依次迭代N次,
            在這種情況下,如果一個(gè)鏈表過(guò)大,那么這里就有效率問(wèn)題.

            一般情況下,我們只對(duì)"類似于數(shù)組的可以隨機(jī)訪問(wèn)"的std容器進(jìn)行排序.

            呵呵,我就喜歡大家這種踴躍討論的氛圍,互相學(xué)習(xí):)

            上面的代碼你在VC6下面測(cè)試通過(guò)了,因?yàn)閺奈业牡谝桓杏X(jué)來(lái)看,必定:coredump.
            當(dāng)時(shí)我還真不太相信,所以我自己也測(cè)試了一下,結(jié)果如下:
            Win32: VC2005 debug/release下均可運(yùn)行正常,不過(guò)因?yàn)閙emset非POD,出現(xiàn)內(nèi)存泄漏.
            Win32: MinGW Studio 直接abort.(這是我預(yù)期的結(jié)果).

            Linux: Slackware32/GCC 直接abort.(這也是我預(yù)期的結(jié)果).

            為了證明上在win32/VC2005下上面的代碼出現(xiàn)內(nèi)存泄漏,大家可以用下面的代碼做測(cè)試:
            (這里把小蘇同學(xué)的代碼取了一部分用于配合測(cè)試)

            運(yùn)行下面的代碼,大家在任務(wù)管理器中觀察內(nèi)存增長(zhǎng)情況:)


            #include "Windows.h"

            #include <string>
            #include <cstring>
            #include <cstdlib>

            using namespace std;

            typedef unsigned int UINT32;
            typedef unsigned short UINT16;

            typedef struct structMan
            {
            UINT32 sexType; //ENUM_SEXY_TYPE
            UINT16 usAge;
            string strName;
            string strAddress;

            bool operator < (const structMan &man) const
            {
            return usAge < man.usAge;
            }

            bool operator > (const structMan &man) const
            {
            return usAge > man.usAge;
            }
            }MAN;

            int main( int argc , char *argv[] )
            {

            while( true )
            {
            MAN man;
            memset( &man , 0 , sizeof( MAN ) );
            man.strAddress = "abcdef";
            man.strName = "abc";
            Sleep( 10 ); //這里sleep是為了讓大家有時(shí)間在任務(wù)管理器中看到內(nèi)存增長(zhǎng)的過(guò)程,不至于一下子耗盡內(nèi)存.
            }


            return 0;
            }
            @小蘇
            sorry,沒(méi)有注意到最后一句話"注意: 以上代碼在VC6環(huán)境下編譯、測(cè)試通過(guò)".
            我所指出的bug依然存在,不同的編譯器對(duì)"memset 非POD處理方式可能不一樣".
            即使VC6測(cè)試通過(guò),你可以換個(gè)編譯器試試.
            看完代碼,給我的第一感覺(jué):代碼存在嚴(yán)重的bug(不知道你自己測(cè)試過(guò)沒(méi)有).
            簡(jiǎn)單的說(shuō)就是"不要對(duì)非POD類型進(jìn)行memset操作".
            在C++中不要對(duì)class進(jìn)行memset操作;盡量不要對(duì)struct進(jìn)行memset操作.
            @qiaojie
            謝謝賜教!

            "像std::invalid_arguement基本沒(méi)人會(huì)去用"這句話說(shuō)得有點(diǎn)絕對(duì)了,用的人應(yīng)該還是有一些的,可能我們沒(méi)有接觸到.
            另外,我們總是被"天朝"代表,想不到這次被 qiaojie 代表了:)

            你說(shuō)"保證參數(shù)的正確性是調(diào)用者的責(zé)任,而不是被調(diào)用的函數(shù)的責(zé)任",
            這一點(diǎn)我也同意,不過(guò)我覺(jué)得作為函數(shù)的設(shè)計(jì)者,我們不應(yīng)當(dāng)對(duì)用戶所傳遞的參數(shù)有太多理想化的假設(shè),所以我們應(yīng)當(dāng)在函數(shù)中進(jìn)行參數(shù)合法性的檢查,
            一方面,可以在函數(shù)的入口處盡早發(fā)現(xiàn)非法參數(shù)的問(wèn)題,這樣就不至于后續(xù)會(huì)使用錯(cuò)誤的參數(shù)在函數(shù)中進(jìn)行一些無(wú)意義的操作.
            另一方面,在函數(shù)入口處檢查參數(shù)的合法性,可以增強(qiáng)函數(shù)的健壯性,進(jìn)一步增強(qiáng)系統(tǒng)的健壯性.
            舉個(gè)例子,如果傳遞給函數(shù)的實(shí)參不應(yīng)該是NULL指針,用戶卻以NULL作為實(shí)參調(diào)用函數(shù),假設(shè)我們沒(méi)有進(jìn)行對(duì)應(yīng)參數(shù)合法性檢查,
            那么后續(xù)基于這個(gè)NULL實(shí)參的操作可能會(huì)導(dǎo)致系統(tǒng)"coredump".

            對(duì)于參數(shù)的合法性檢查,在debug版本和release版本下應(yīng)該都需要進(jìn)行,類似于"assert(0 < age && age < 200);"這種檢測(cè)參數(shù)的合法性的代碼只在debug版本下可以起作用,
            在release版本下就不起用了,也就不能在release版本下作為參數(shù)合法性檢查的工具.
            在debug版本下,如果assert斷言失敗,那么我們可以看到對(duì)應(yīng)的abort信息,然后程序異常退出.
            實(shí)際上這樣做可能有的時(shí)候并不合適,因?yàn)樵谝恍┣闆r下,僅僅是參數(shù)非法,我們可以進(jìn)行相應(yīng)的處理而不需要系統(tǒng)因此而退出運(yùn)行.

            "強(qiáng)調(diào)不要用異常或者錯(cuò)誤返回值,是因?yàn)槊つ康拇罅渴褂眠@類錯(cuò)誤處理機(jī)制會(huì)導(dǎo)致整個(gè)項(xiàng)目變得混亂"
            這句話如果僅僅是理論上來(lái)探討"如何讓系統(tǒng)設(shè)計(jì)的更優(yōu)雅",那么這無(wú)疑可以作為一個(gè)"系統(tǒng)設(shè)計(jì)準(zhǔn)則",
            但是在實(shí)際的開(kāi)發(fā)過(guò)程中,有的時(shí)候一個(gè)函數(shù)內(nèi)部出現(xiàn)"非正常情況"的可能性實(shí)在是太多了,我們必須要進(jìn)行相應(yīng)的處理.
            如果我們既不使用"異常"也不使用"返回錯(cuò)誤碼"的形式來(lái)告知調(diào)用者,
            那么在反饋給調(diào)用者"函數(shù)內(nèi)部出現(xiàn)非正常情況"這一點(diǎn)上我們將"無(wú)能為力",但我們又必須在這一點(diǎn)有所作為.

            在大多數(shù)情況下,"異常"和"錯(cuò)誤碼"可能是我們僅有的兩個(gè)選擇方案,如何選擇其一作為最終的處理方案,
            甚至如何在不使用"異常"和"錯(cuò)誤碼"的前提下也達(dá)到相同的效果,這是一件很"糾結(jié)"的事情.


            追求系統(tǒng)在架構(gòu)和代碼設(shè)計(jì)上的完美是開(kāi)發(fā)者的一個(gè)方向,但是有時(shí)我們需要考慮"追求完美的代價(jià)",
            在時(shí)間,人力以及成本的多重影響下,很多時(shí)候我們必須放棄對(duì)最優(yōu)方案的探索,而選擇一種"不那么完美但是可行,可以很好解決問(wèn)題"的方案.
            也許這個(gè)時(shí)候作為函數(shù)調(diào)用狀態(tài)反饋的"異常"和"錯(cuò)誤碼"機(jī)制會(huì)在我們的思考和運(yùn)用范圍之內(nèi).


            @qiaojie
            也許你的異常哲學(xué)是正確的并值得大家學(xué)習(xí),還請(qǐng)你發(fā)文一篇讓大家有一個(gè)學(xué)習(xí)的機(jī)會(huì),
            如果從你的文章中我確實(shí)發(fā)現(xiàn)了自己的錯(cuò)誤,也會(huì)從中有所改正,當(dāng)然,你也不需要"對(duì)牛彈琴"這個(gè)詞語(yǔ).
            我這篇文章中的"People"只是一個(gè)用于作為討論基礎(chǔ)的例子,根本的問(wèn)題是對(duì)于"構(gòu)造函數(shù)的參數(shù)非法或是構(gòu)造失敗"時(shí),我們應(yīng)當(dāng)如果告知調(diào)用者.
            我并沒(méi)有說(shuō)一定要把參數(shù)的合法性全部放在構(gòu)造函數(shù)中完成,但是在構(gòu)造函數(shù)中檢查參數(shù)的合法性是應(yīng)該的,
            就像上面的同學(xué)說(shuō)的"為了程序的健壯性,多余的操作也是必須的"。
            在這個(gè)例子中,你可以往自己熟悉的GUI方向進(jìn)行特化,所以你可以使用"對(duì)話框"之類的工具來(lái)進(jìn)行傳入構(gòu)造函數(shù)之前的參數(shù)合法性檢驗(yàn)以及進(jìn)行相關(guān)的錯(cuò)誤處理,
            但是在那些"非GUI"的領(lǐng)域,在那些"我們不能確保傳入構(gòu)造函數(shù)的參數(shù)一定是合法的,不能保證構(gòu)造函數(shù)一定會(huì)構(gòu)造成功"的情況下,我們到底該如何處理,
            我考慮到可以使用"基于異常"或"基于兩段構(gòu)造的形式".
            C++提供的異常機(jī)制是一種工具,可以作為"函數(shù)內(nèi)部向函數(shù)的調(diào)用者傳遞函數(shù)內(nèi)部非正常運(yùn)行狀態(tài)"的一種方法.
            就如同你說(shuō)的"內(nèi)存耗盡,網(wǎng)絡(luò)錯(cuò)誤,文件錯(cuò)誤"這種情況下是異常,也許這種情況下我們應(yīng)當(dāng)使用"異常機(jī)制"(希望沒(méi)有理解錯(cuò)).
            但是如果一個(gè)函數(shù)內(nèi)部可能出現(xiàn)"內(nèi)存耗盡"也會(huì)出現(xiàn)"參數(shù)非法的問(wèn)題"(再重申一遍,我們不能永遠(yuǎn)都保證傳入每一個(gè)函數(shù)的參數(shù)都是合法的).
            "內(nèi)存耗盡"這種情況我們使用異常,但是"參數(shù)非法問(wèn)題"我們使用什么呢,
            按照你的看法,"參數(shù)非法"不屬于異常的范圍之內(nèi),我們不應(yīng)該使用"異常的形式",但我們還是要告知用戶"參數(shù)非法"的信息,
            假定這里我們"無(wú)法使用類似于彈出對(duì)話框的形式來(lái)告知用戶參數(shù)非法",那么我可以想到的告知調(diào)用者這一信息的方式是"使用錯(cuò)誤碼",
            當(dāng)然,我們還可以選擇"errno"的形式.
            這樣一來(lái),我們就面臨一個(gè)問(wèn)題"一個(gè)函數(shù)會(huì)以異常和錯(cuò)誤碼兩種方式來(lái)告知調(diào)用者相關(guān)的非正常運(yùn)行信息",
            接下來(lái),調(diào)用者就要同時(shí)使用"try catch"和檢查函數(shù)的錯(cuò)誤碼兩種方式來(lái)檢查函數(shù)的運(yùn)行狀態(tài),
            我覺(jué)得如果真的這樣設(shè)計(jì)函數(shù)的話,這就是一種很糟糕的設(shè)計(jì),不知道你怎么認(rèn)為.

            在告知調(diào)用者一個(gè)函數(shù)內(nèi)部的"非正常狀態(tài)"時(shí),我只會(huì)擇優(yōu)使用"錯(cuò)誤碼"或"異常這兩種形式"之一,不會(huì)同時(shí)使用.
            基于這一點(diǎn),如果我選擇"以錯(cuò)誤碼的形式"來(lái)反饋給調(diào)用者,那么在函數(shù)內(nèi)部"網(wǎng)絡(luò)錯(cuò)誤"時(shí)我也會(huì)使用錯(cuò)誤碼來(lái)告知調(diào)用者(按你的看法,這種情況應(yīng)該使用異常),
            如果我選擇"基于異常"的形式,那對(duì)"參數(shù)非法"的信息我也會(huì)拋出"std::invalid_arguement".這是設(shè)計(jì)上的取舍產(chǎn)生的必然選擇.


            說(shuō)到這里,不知道你對(duì)于作為std異常類型之一的"std::invalid_arguement"這個(gè)詞語(yǔ)有什么感想,
            我覺(jué)得你應(yīng)該向標(biāo)準(zhǔn)委員會(huì)指明"std::invalid_arguement"這個(gè)詞語(yǔ),
            "從使用異常的哲學(xué)上的角度上來(lái)看這個(gè)概念是錯(cuò)誤的,因?yàn)閰?shù)非法根本就不是異常,我們又怎么能因?yàn)閰?shù)的非法而throw std::invalid_arguement,
            這是在誤導(dǎo)廣大的std用戶,所以必須去掉".
            @飯中淹
            將"兩段構(gòu)造"與"Fatcory Pattern"結(jié)合起來(lái)確實(shí)是一種巧妙的設(shè)計(jì)!
            內(nèi)部實(shí)現(xiàn)上還是"兩段構(gòu)造",但是對(duì)于 class 的用戶而言,class CPeople 展現(xiàn)的卻是一個(gè)單一的"構(gòu)造接口",
            用戶一旦調(diào)用這個(gè)接口"構(gòu)造對(duì)象",那么"兩段構(gòu)造"自動(dòng)完成,極大地減少了"兩段構(gòu)造"中因?yàn)橥浾{(diào)用"Initialize"所帶來(lái)的問(wèn)題.
            class CPeople 中的 Create 和 Release 所扮演的角色類似于"構(gòu)造函數(shù)和析構(gòu)函數(shù)",都是進(jìn)行資源的分配與回收操作.
            單純從"資源管理"的角度來(lái)說(shuō),肯定是"構(gòu)造函數(shù)和析構(gòu)函數(shù)"相比如"Create 和 Release"更優(yōu)一些,
            因?yàn)?quot;構(gòu)造函數(shù)和析構(gòu)函數(shù)"對(duì)于"非動(dòng)態(tài)分配的對(duì)象以及非placement new方式生成的對(duì)象",
            構(gòu)造和析構(gòu)都會(huì)由編譯器保證正確自動(dòng)地調(diào)用,大大簡(jiǎn)化了對(duì)資源的管理,或許這也是C++設(shè)計(jì)構(gòu)造和析構(gòu)的出發(fā)點(diǎn)之一.

            在"兩段構(gòu)造" & "Fatcory Pattern"這種模式下,所有的CPeople對(duì)象將都由 Create 接口創(chuàng)建,這勢(shì)必需要我們管理大量的動(dòng)態(tài)分配的對(duì)象,
            在這種情況下,如果稍有不慎,我們將面臨"resource leak"的問(wèn)題.這個(gè)時(shí)候如果我們能將動(dòng)態(tài)分配的CPeople對(duì)象用一種更方便安全的方式來(lái)管理就更好了,
            于是我想到了boost::shared_ptr,不知道大家想到了什么?
            類似于下面這樣:

            void FreeResource( CPeople *pPeople )
            {
            if( NULL != pPeople )
            {
            pPeople -> Release();
            }
            }

            CPeople *pHebe = CPeople::Create( 2 );
            if( NULL == pHebe )
            {
            // handle error
            }

            boost::shared_ptr< CPeople > pPeople( pHebe , FreeResource );

            下面我們就可以使用 pPeople 這個(gè)智能指針"do whatever you want" :) ,而且使用起來(lái)直觀方便:
            pPeople -> Sing();
            也減少了對(duì)動(dòng)態(tài)分配資源進(jìn)行管理的復(fù)雜度.



            @qiaojie
            呵呵,可不能一棒子打死啊.
            至于說(shuō),類似于"用戶輸入非法數(shù)據(jù)"之類的問(wèn)題到底是算作錯(cuò)誤還是異常情況,這一點(diǎn)依賴每個(gè)人對(duì)同一個(gè)事物的認(rèn)知,
            有的人認(rèn)為這是異常情況,有的人認(rèn)為這是錯(cuò)誤,這個(gè)認(rèn)知層面上的問(wèn)題我們先不討論,尊重每個(gè)人的看法.
            實(shí)際上,即使存在上面的認(rèn)知差異也沒(méi)有關(guān)系,因?yàn)閱?wèn)題的本質(zhì)是對(duì)"用戶輸入非法數(shù)據(jù)"這種異常也好,錯(cuò)誤也好,
            我們?cè)诖a邏輯中應(yīng)該如何處理,你覺(jué)得應(yīng)該用類似于"對(duì)話框+ValidateUserInput"之類的方法來(lái)處理,
            我覺(jué)得可以通過(guò)返回錯(cuò)誤碼或拋出異常的形式來(lái)做處理. 本質(zhì)上都是在處理一種"非正常情況",只是我們的處理方式不同,
            你說(shuō)應(yīng)該用你的方法比較好,我覺(jué)得用我的方法處理也是可行的,到底用哪一種呢,
            即使在這種情況下,我們還是很難選擇一個(gè)所有人都接受的處理方式. 這里就涉及到設(shè)計(jì)的權(quán)衡和取舍了,有很多種方法都可行,我們尊重每個(gè)人在特定的環(huán)境中所做出的選擇.

            /*
            "而是在用戶輸入完成,對(duì)話框結(jié)束,構(gòu)造People之前,加入一個(gè)ValidateUserInput()函數(shù)來(lái)校驗(yàn)用戶輸入,
            如果age屬于非法值,彈出一個(gè)錯(cuò)誤對(duì)話框向用戶說(shuō)明錯(cuò)誤的原因"
            */
            你這里只是對(duì)一種特例的處理,實(shí)際上我們很難在所有的情況都保證傳入構(gòu)造函數(shù)的參數(shù)是合法的,要是我們真的找到了這樣一種方法,
            那么"there is a silver bullet !" , 接下來(lái),對(duì)于所有奮斗在開(kāi)發(fā)一線的同學(xué)們而言,生活就要美好很多,應(yīng)該再也不會(huì)發(fā)生類似于"小貝"的悲劇了:)

            在你處理的特例中,既然我們能夠保證傳入構(gòu)造函數(shù)的參數(shù)一定是合法的,那確實(shí)太好了,"使用異常"和"兩段構(gòu)造"都是多余的.
            對(duì)于那種我們不能確保傳入構(gòu)造函數(shù)的參數(shù)是一定是合法的情況,我們?cè)撨x擇哪種處理方式呢,這是這篇文章討論的根本問(wèn)題.
            如果因?yàn)闃?gòu)造函數(shù)的參數(shù)不合法,或者因?yàn)槠渌脑驑?gòu)造失敗,最基本的一點(diǎn),我們應(yīng)當(dāng)讓調(diào)用者知道這一情況,
            至于調(diào)用者如何處理就不在我們關(guān)心的范圍之內(nèi)了,是"彈出對(duì)話框告知用戶重試","忽略這個(gè)錯(cuò)誤",還是直接"abort",不同的場(chǎng)景下也有不用的選擇.
            我們要做的就是在"構(gòu)造一個(gè)對(duì)象發(fā)生異常時(shí)"告知調(diào)用者"發(fā)生了非正常情況".

            這篇文章的主題也就是討論"在構(gòu)造發(fā)生非正常情況時(shí)采取何種方式來(lái)告知調(diào)用者這一情況".
            對(duì)于這個(gè)問(wèn)題很難有一個(gè)"放之四海而皆準(zhǔn)"的處理方案,因?yàn)檫@涉及到不同的編程風(fēng)格,應(yīng)用場(chǎng)景和設(shè)計(jì)時(shí)的取舍.

            不過(guò)我們還是可以踴躍地發(fā)表自己的看法,在討論和交流的過(guò)程中我們總能發(fā)現(xiàn)思維的閃光點(diǎn).互相學(xué)習(xí):)
            @Corner Zhang
            關(guān)于這一點(diǎn),我們的觀點(diǎn)還是很相近的,不同的編程風(fēng)格決定了不同的設(shè)計(jì)取舍以及代碼風(fēng)格,
            沒(méi)有哪一種一直都是最優(yōu)的,選擇一種適合自己的并一直堅(jiān)持下去(當(dāng)然,適當(dāng)?shù)臅r(shí)候還是要變通一下).
            實(shí)際上我并不愿意在代碼中大量使用"throw try catch",所以,基本上我不愿意去看java代碼,
            就如你所說(shuō)的,傳統(tǒng)的代碼風(fēng)格易于跟蹤調(diào)試;在我看來(lái),傳統(tǒng)的"基于錯(cuò)誤碼"的代碼比基于"使用異常"的代碼
            要緊湊得多,因?yàn)槲覀兛梢栽阱e(cuò)誤發(fā)生的地方立即處理錯(cuò)誤,而不像"基于異常"的代碼中我們要向下跨越N行代碼
            來(lái)進(jìn)行錯(cuò)誤處理(這一點(diǎn)使得代碼的可讀性很差).
            而且,如果try代碼塊中太大,那么在對(duì)應(yīng)的catch塊中盡管我們可以進(jìn)行相應(yīng)的異常處理,但是此時(shí)我們卻失去了
            對(duì)發(fā)生錯(cuò)誤的代碼上下文的必要了解.這一點(diǎn)使得我們降低了對(duì)代碼整體運(yùn)行流程的可預(yù)知性,
            更重要的是也降低了錯(cuò)誤處理的針對(duì)性,因?yàn)橥环N類型的異常可能由try代碼塊中的多個(gè)地方throw.具體是哪一個(gè)throw的無(wú)從了解.
            so,我的觀點(diǎn)是:讓構(gòu)造函數(shù)盡量簡(jiǎn)單,減少誤用的可能性,并增加構(gòu)造函數(shù)的安全性(盡量減少構(gòu)造函數(shù)構(gòu)造失敗的可能性).
            這樣我們也就能在一定程度上減少對(duì)異常機(jī)制的依賴.至于其它的可帶有返回值的成員函數(shù)都使用"返回錯(cuò)誤碼"來(lái)取代"拋出異常".
            我覺(jué)共享內(nèi)存的這種"非顯示刪除自保留性"是很有用的特性而不是問(wèn)題,也許當(dāng)時(shí)設(shè)計(jì)共享內(nèi)存機(jī)制時(shí)這種特性是有意提供的.
            設(shè)想一個(gè)簡(jiǎn)單的例子:
            我們使用一塊有訪問(wèn)控制保護(hù)的內(nèi)存緩沖區(qū)來(lái)存儲(chǔ)進(jìn)程或線程之間共用的的數(shù)據(jù).
            并且我們需要保證數(shù)據(jù)的安全性,即使server倒掉,這塊緩沖區(qū)里面的數(shù)據(jù)也不能丟失.
            1. 對(duì)于多線程的情況,當(dāng)一個(gè)線程core掉之后(比如因?yàn)閟egment fault),線程所對(duì)應(yīng)的進(jìn)程將不能幸免.
            在這種情況下,如果我們使用的是一般的"內(nèi)存緩沖區(qū)"而不是共享內(nèi)存,那么當(dāng)進(jìn)程退出后,
            這塊緩沖區(qū)對(duì)應(yīng)的內(nèi)存空間將會(huì)被系統(tǒng)回收重新分配給其它進(jìn)程使用,緩沖區(qū)中對(duì)應(yīng)的數(shù)據(jù)也就丟失了.
            但是如果我們換用共享內(nèi)存來(lái)作為線程間交換數(shù)據(jù)的緩沖區(qū),我們就能很好的解決這個(gè)問(wèn)題.
            2. 在多進(jìn)程的情況下,不但能滿足上述特性,而且共享內(nèi)存也是進(jìn)程間數(shù)據(jù)交換的一種高效方式.
            至于共享內(nèi)存的手動(dòng)刪除問(wèn)題,我的做法是,在生成IPC對(duì)象時(shí),將對(duì)應(yīng)的ftok生成的key值寫(xiě)入到一個(gè)文件中,
            生成類似于下面的shell腳本,這樣當(dāng)我們需要手動(dòng)刪除IPC時(shí)也很方便:

            ipcrm -M xxx
            ipcrm -Q xxx
            ipcrm -S xxx

            相比于通過(guò)socket來(lái)作為進(jìn)程間通信的方式,共享內(nèi)存的最大不足在于"不能跨機(jī)器共用".
            after all, there is no silver bullet :)
            你的代碼中是通過(guò)判斷信號(hào)量的值為0來(lái)作為對(duì)共享內(nèi)存讀取操作的依據(jù),即是如下代碼:
            wait_v(semid);
            printf("Message geted is: %s \n",shm + 1);

            但實(shí)際上這里有一個(gè)潛在的問(wèn)題:
            即如果 wait_v(semid); 成功后,在執(zhí)行接下來(lái)的printf("Message geted is: %s \n",shm + 1)之前,進(jìn)程被掛起.
            那么此時(shí) server 進(jìn)程可能會(huì)重新獲取這個(gè)信號(hào)量并對(duì)共享內(nèi)存中的數(shù)據(jù)進(jìn)行寫(xiě)操作(當(dāng)然,你這里用server sleep的時(shí)間遠(yuǎn)大于client sleep的時(shí)間來(lái)解決這個(gè)問(wèn)題)
            當(dāng)掛起的進(jìn)程重新被調(diào)度投入運(yùn)行后,此時(shí)printf("Message geted is: %s \n",shm + 1)的數(shù)據(jù)實(shí)際上就不是wait_v(semid)成功后共享內(nèi)存中對(duì)應(yīng)的數(shù)據(jù).

            我覺(jué)得對(duì)于這種典型的 "provider/consumer" 模型,一種更好的做法是
            // for provider // for consumer
            P( write_sem ); P( read_sem );

            // write operation // read operation

            V( read_sem ); V( write_sem );


            當(dāng)然,這里我們需要使用 write_sem 和 read_sem 兩個(gè)信號(hào)量.
            @阿福:
            一直都把truncate用作截?cái)辔募瑳](méi)有發(fā)現(xiàn)truncate還可以用于擴(kuò)展文件大小,剛才看了一下 man 文檔:
            int truncate(const char *path, off_t length);
            If the file previously was larger than length, the extra data is discarded.
            If the file was previously shorter than length, its size is increased, and the extended area appears as if it were zero-filled.
            這樣一來(lái),截?cái)嗪蛿U(kuò)展文件都可以用"truncate"來(lái)完成,這樣相對(duì)于上面的EnlargeFile就更簡(jiǎn)潔了而且基于"truncate"的形式只需要一次系統(tǒng)調(diào)用即可實(shí)現(xiàn)相同的效果,效率上也更有優(yōu)勢(shì).
            看來(lái)這次真的是"reinvent the wheel"了:(
            thank you for reminding me.
            最后分享一個(gè) linux 2.6 的 man pages 打包成的CHM文件,在上面的下載文件中.
            謝謝指教!
            以前在Win32下做過(guò)一段時(shí)間,深感Microsoft的巨大努力給我們帶來(lái)的便利:) 海量的MSDN和豐富的Win32 API讓我們遇到問(wèn)題有據(jù)可查,
            同時(shí)也減少了大量"reinvent the wheel"的時(shí)間.
            但是到了*nix下面,很多東西都不一樣了,*nix的哲學(xué)是"提供解決問(wèn)題的機(jī)制而不是具體的實(shí)現(xiàn)",相反,Win32的哲學(xué)是"提供具體的實(shí)現(xiàn)但是不告訴你具體的機(jī)制"。
            所以這篇文章的出發(fā)點(diǎn)就是"利用Linux提供的機(jī)制來(lái)解決一個(gè)實(shí)際的問(wèn)題",形式上與Win32的"SetFilePointer & SetEndOfFile"組合不一樣,但是仔細(xì)分析一下,
            它們是如此的相似:按照你的建議"先調(diào)用 SetFilePointer(設(shè)置文件邏輯指針位置) 然后調(diào)用 SetEndOfFile(設(shè)置文件物理末端位置)"即可快速擴(kuò)展文件大小。
            在上面的代碼中將參數(shù)合法性判斷以及對(duì)應(yīng)的函數(shù)調(diào)用狀態(tài)判斷去掉,簡(jiǎn)化一下就是下面這樣了:

            bool EnlargeFile( int iFileHandle , off_t iNewSize )
            {

            1. lseek( iFileHandle , 0 , SEEK_CUR ); //保存文件指針的當(dāng)前位置以便于在擴(kuò)展文件大小后恢復(fù)到當(dāng)前位置
            2. lseek( iFileHandle , iMoveOffset , SEEK_SET );
            3. write( iFileHandle , " " , WRITE_BYTE_COUNT ); //寫(xiě)入一個(gè)字節(jié)的數(shù)據(jù),完成對(duì)文件大小的更改
            4. lseek( iFileHandle , iCurPos , SEEK_SET ); //恢復(fù)文件指針到之前保存的文件位置

            return true;
            }

            其中的第1行和第4行是為了在擴(kuò)展文件的過(guò)程中保存和恢復(fù)文件指針位置,如果我們將這一點(diǎn)也簡(jiǎn)化掉(實(shí)際上必須保留),如下:
            bool EnlargeFile( int iFileHandle , off_t iNewSize )
            {
            // 設(shè)置文件指針(這里是邏輯指針)位置,相當(dāng)于Win32下調(diào)用 SetFilePointer
            2. lseek( iFileHandle , iMoveOffset , SEEK_SET );

            // 寫(xiě)入一個(gè)字節(jié)的數(shù)據(jù),完成對(duì)文件大小的更改,即是設(shè)置了文件的物理末端指針位置,相當(dāng)于調(diào)用了 SetEndOfFile
            3. write( iFileHandle , " " , WRITE_BYTE_COUNT );

            return true;
            }

            這一次很清晰了,形式上不一樣,但是本質(zhì)上很相近了。

            潮喷大喷水系列无码久久精品| 国产一区二区久久久| 国内精品久久人妻互换| 久久久久99精品成人片直播| 亚洲综合久久综合激情久久| 精品无码久久久久久久久久| 久久夜色精品国产亚洲| 国产精品久久久久久福利69堂| 欧美国产成人久久精品| 久久久久99精品成人片欧美| 久久久久99精品成人片| 久久精品黄AA片一区二区三区| 久久久久久国产精品无码下载 | 久久综合亚洲色HEZYO社区| 精品人妻久久久久久888| 四虎国产精品成人免费久久| 国内精品久久久久影院一蜜桃| 亚洲国产精品综合久久网络 | 久久人妻少妇嫩草AV无码蜜桃| 亚洲精品午夜国产VA久久成人| 国产激情久久久久影院小草| 久久综合国产乱子伦精品免费| 久久人人超碰精品CAOPOREN| 91久久精品电影| 久久国产乱子伦精品免费强 | 色综合久久综合网观看| 久久亚洲精品成人av无码网站| 久久综合亚洲色HEZYO国产| 伊人色综合久久| 国产99久久久久久免费看| 777米奇久久最新地址| 久久亚洲国产成人精品性色| 国产69精品久久久久9999APGF| 伊人伊成久久人综合网777| 免费精品久久久久久中文字幕| 久久精品国产欧美日韩| 久久综合亚洲色HEZYO国产| 欧美国产精品久久高清| 久久久噜噜噜久久中文字幕色伊伊| 久久嫩草影院免费看夜色| 亚洲乱码日产精品a级毛片久久|