青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

huaxiazhihuo

 

消息發(fā)送VS設(shè)計模式 C++沉思錄的一練習(xí)題的另解

     摘要:         緣起,看到一遍文章,題材取自于《C++沉思錄》,綜合利用好幾種設(shè)計模式,并且結(jié)合所謂的面向?qū)ο蟮募记桑缓笱笱笞缘茫⑶以谧詈螅€反問:“有更好的解決方案嗎?”。本座暗嘆,又一個設(shè)計模式的毒害者。以下,就提出另一種解決方案。      &...  閱讀全文

posted @ 2012-06-12 17:16 華夏之火 閱讀(2705) | 評論 (8)編輯 收藏

基于堆棧上的字符串實現(xiàn)

           C++中,由于字符串一開始并非內(nèi)置的類型,于是,史前時代,江湖上出現(xiàn)了種種的字符串,各有各的優(yōu)點,但自然,也各有各的缺陷,群雄割據(jù),天下大亂,民不聊生。大伙兒盼星星,盼月亮,盼著真正的字符串能出來一統(tǒng)江湖,好不容易,等到1998C++標(biāo)準(zhǔn)出來了,官方的字符串string終于露面了,自然,它并不是語言內(nèi)置支持的,而是在STL庫中。當(dāng)然,字符串這東西,就算要在C++編譯器的層面上給予支持,其實也很不容易。可是,這個官方的string,單純它復(fù)雜的模板定義和那一大陀函數(shù)成員,就足以嚇退眾多意志不堅定之人。好了,好不容易,鼓起勇氣,再仔細(xì)瞅瞅string中的東西,發(fā)現(xiàn)它也不是很美妙,既不強大,部分函數(shù)的功能存在重復(fù)又或者STL的算法中已有提供,更要命的是,效率也不怎么高。總之,不能說string的設(shè)計不如史前的各種stringS,但也強不到那里去。當(dāng)然,官方的總比非官方的更具權(quán)威,但每次使用string時,想到它背地里除了正常字符串操作之外,還可能做了各種的低效的內(nèi)存分配釋放的操作,又或者線程安全,又或者引用計數(shù),內(nèi)心就一直惴惴不安。于是,寧愿一再小心翼翼地用著字符數(shù)組。但是,用得多了,也很郁悶,字符數(shù)組自然高效、靈活,但總是要千編一律地一再寫著容易出錯的代碼,我脆弱的心靈終于頭暈眼花了。于是,我決定按照自己的意愿寫一個字符串,不敢欲與群雄爭鋒,只是,為了能夠在自己的代碼中,替換字符數(shù)組。我對它的要求是,字符數(shù)組能做到的事情,它也要做到,并且,效率上,絕不妥協(xié)。

          俗話說,過早的優(yōu)化是萬惡之源。但在此,在設(shè)計本家字符串時,一開始,就要考慮到效率的細(xì)節(jié)上去了。首先,它要支持堆棧變量的形式,不要它進(jìn)行內(nèi)存的分配釋放操作,就好像堆棧上的字符數(shù)組那樣。咦,好像很神奇,其實,只要想到TR1中那個經(jīng)典的array,通過使用神奇的模板技術(shù),就有辦法做到了。所以,此字符串的使用好比這樣子,CStackString <MAX_PATH> sFile,暫時假定這個字符串的名字叫CStackString

          但是,使用模板之后,字符串的字符數(shù)組的長度只要不一樣,它們就都屬于不同類型變量,并且之間還都不兼容呢,雖然它們都是字符串。此外,還會編譯器還將生產(chǎn)出一堆重復(fù)的代碼。這無論如何,都不能忍受。于是,自然而然,就想到了繼承。CStackString模板類繼承于非模板的mybasestringmybasestring中實現(xiàn)了CStackString的各種各樣的操作,而CStackString只要仿照array那樣子,定義好自己的數(shù)據(jù)成員即可。嗯,還是看看代碼,馬上就明白到底是怎么一回事了。


class CMyBaseString
{
public:
    typedef size_t size_type;
    typedef 
char *pointer;
    typedef 
const char *const_pointer;
    typedef CMyBaseString _Myt;

public:
    
char operator[](size_type nPos)const
    
{
        assert(nPos 
< m_nLen);
        
return m_str[nPos];
    }


    const_pointer c_str()
const return m_str; }

    const_pointer right(size_type nLen)
const
    
{
        assert(nLen 
< m_nLen);
        
return m_str+m_nLen-nLen;
    }


    
int compare(const_pointer str)const
    
{
        
return strcmp(m_str, str);
    }


    _Myt
& assign(const char* str)
    
{
        m_nLen 
= strlen(str);
        assert(m_nLen 
< m_nBuffSize);
        strcpy(m_str, str);
        
return *this;
    }


    _Myt
& append(const_pointer str)
    
{
        size_type nLen 
= strlen(str);
        assert(m_nLen 
+ nLen < m_nBuffSize);
        strcpy(m_str
+m_nLen, str);
        m_nLen 
+= nLen;
        
return *this;
    }


    _Myt
& format(const_pointer sFormat,  )
    
{
        va_list argList;
        va_start( argList, sFormat );
        m_nLen 
= vsprintf(m_str, sFormat, argList);
        va_end( argList );
        assert(m_nLen 
< m_nBuffSize);
        
return *this;
    }


    
//.

protected:
    CMyBaseString(pointer sBuf, size_type nBuffSize)
    
{
        m_nBuffSize 
= nBuffSize;
        m_nLen 
= 0;
        m_str 
= sBuf;
        m_str[
0= 0;
    }


private:
    size_type m_nBuffSize;
    size_type m_nLen;
    pointer m_str;
}
;

template
<size_t _size>
class CStackString : public CMyBaseString
{
public:
    CStackString(
const char* str) : CMyBaseString(m_sMine, _size) { assign(str);}
    CStackString() : CMyBaseString(m_sMine, _size) 
{}

private:
    
char m_sMine[_size];
}
;
int main()
{
    CStackString
<20> sTest("hello");
    cout 
<< sTest.c_str() << endl;
    cout 
<< sTest.right(3<< endl;
    
return 0;
}

          于是通過基類mybasestring,各種不同類型的template CStackString就又聯(lián)系在一塊了。Mybasestring可看成定義了數(shù)據(jù)接口的基類,其子類的頭部數(shù)據(jù)必須與它保持一致,嗯,很好。然后,在mybasestring中實現(xiàn)的各種功能,都可以用在mystring身上了,而CStackString中無須實現(xiàn)任何功能,它只負(fù)責(zé)在堆棧上分配內(nèi)存。所以,mybasestring不僅可以用在堆棧上,還能用于堆上,只要再繼續(xù)定義一個能在堆上分配內(nèi)存的mybasestring的子類即可,然后都能相容于堆棧上的CStackString字符串。

          ……。 經(jīng)過一番努力,這個字符串類幾乎包含了字符數(shù)組的一切基本功能,基本上可代替字符數(shù)組了。為了能夠用到STL中的各種算法,再在其上增加一些返回迭代器的函數(shù),好比beginendrbeginrend,它們都很容易實現(xiàn)。還有,為了使用起來更加友好方便更具效率,貌似應(yīng)該再實現(xiàn)一些全局操作符的重載運算;……;好了,打住。如果打算將這個字符串很好地融入到STL中,需要做出更多的努力。原本只打算代替字符數(shù)組而已。代碼在VC2005以上版本編譯時,會出現(xiàn)一些警告,可以用#pregma的指令將其disable掉,或者使用其中的所謂的安全字符串操作函數(shù)。

          好不容易,終于就實現(xiàn)了一個基于堆棧上的字符串,它是窄字符的。別忘了,還有寬字符的字符串呢。這也沒什么,只須將mybasestring重命名為CMyBaseStringACStackString改為CStackStringA。然后再分別實現(xiàn)與CMyBaseStringACStackStringA同樣接口的CMyBaseStringWCStackStringW。然后,再針對UNICODETypedefdefined一對CMyBaseStringTCStackStringT。在這里,并不想模仿STL中的stringMFC中的CString那樣子,template一個basic_stringsimple_string),然后分別進(jìn)行模板特化,近來越看越覺得這種模板特化的方式相當(dāng)惡心,只是將代碼搞得更加復(fù)雜,卻沒帶來多大的好處。

          相比于其他的字符串實現(xiàn),這個mybasestring不過是將內(nèi)存分配拱手讓人罷了。這樣一來,就帶來一些新的問題。首先,它要假設(shè)其子類給它分配了足夠的內(nèi)存,不過,在C++傳統(tǒng),經(jīng)常假設(shè)用戶分配了足夠的內(nèi)存;然后,因為脫離了內(nèi)存管理,有一些功能自然也就無法實現(xiàn)出來了,C的字符串函數(shù)也還不是這樣,當(dāng)緩沖溢出時,該崩潰就還得崩潰。

          再次向C++的無所不能頂禮膜拜。C++,你是電,你是光, 你是唯一的神話, 我只愛你,You are my Super Star

          再次聲明,本字符串只為取代字符數(shù)組,至于其它的種種無理要求,均不在本座的考慮范圍之內(nèi)。

posted @ 2012-06-08 01:11 華夏之火 閱讀(2787) | 評論 (13)編輯 收藏

玩具代碼 24點游戲

     摘要:         所謂24點,就是甩出幾個整數(shù),整數(shù)之間沒有固定的前后順序,給它們添加上加減乘除括號等,形成一條式子,最后運算結(jié)果等于24。很自然的想法,就是祭出表達(dá)式樹。但24點只是一個小程序,用表達(dá)式樹來解決,實在有點大材小用了,太委屈表達(dá)式樹了,所以堅決抵制。     &nb...  閱讀全文

posted @ 2012-06-07 16:20 華夏之火 閱讀(1897) | 評論 (1)編輯 收藏

有理數(shù)類的一點思考

        在寫24點的程序時,要處理除法運算,不可避免地將引入小數(shù)點,但本人對于計算機中的浮點數(shù)實在沒有太多的好感。權(quán)衡再三,終于決定下定決心寫一個有理數(shù)類(以下簡稱為CRational),以解決除法運算這個問題。并且有理數(shù)這個類也是一個很好的C++練習(xí)題,可以用來練練手,以下將看到,它的實現(xiàn)雖然不難,但也不是很容易。有理數(shù)這個東西,屬于用戶自定義的數(shù)據(jù)類型,c++對此的支持,真可謂完美,既不失效率,又具備美觀。c++可以讓程序員做出來的自定義類型,其行為可以表現(xiàn)得好像是由語言層面實現(xiàn)的那個樣子,不管從語法、安全、效率上講。這一點,所有的語言都沒法和c++媲美。
        不管怎么說,CRational的需求相當(dāng)明確,它一定有分子、分母、然后支持加減乘除這四種運算,于是,一口氣馬上就能寫下它的定義。
class CRational
{
public:
    CRational(
int nNumberator=0int nDenominator=1);
    
int Numberator()const return m_nNum;}
    
int Denominator()const return m_nDe;}

    CRational
& operator+=(const CRational& _Right);
    CRational
& operator-=(const CRational& _Right);
    CRational
& operator*=(const CRational& _Right);
    CRational
& operator/=(const CRational& _Right);
    CRational 
operator-()const    // 一元操作符,用以取反
    {
        
return CRational(-m_nNum, m_nDe);
    }


private:
    
int m_nNum;
    
int m_nDe;
}
;
         嗯,我承認(rèn)代碼很匈牙利,中MFC的毒太深。毋庸置疑,分子分母只能獲取,不能設(shè)置,函數(shù)名中,沒有加Get的前綴,實在是因為代碼中它出現(xiàn)的地方太多了,所以能避免就盡量避免。沒有涉及資源分配,析構(gòu)函數(shù)可以忽略,拷貝構(gòu)造函數(shù)和賦值函數(shù)也不用寫了,編譯器將會提供缺省的實現(xiàn),足以滿足我們的要求。貌似應(yīng)該還要有一個返回求取小數(shù)值的操作,但是,這個類本身就是為了避免操作小數(shù)點,而且,計算小數(shù)點值可通過分子/分母的方法計算出來。總之,CRational的終極接口就是這個樣子了。
        每個有理數(shù)都有一個標(biāo)準(zhǔn)的等價類,這個標(biāo)準(zhǔn)的有理數(shù)的分子、分母都不能再約分了,而且可以暫時假設(shè)符號位出現(xiàn)于分子中,分母則為正整數(shù),比如說,3/6、-4/-8都等價于1/2,因此,在這個有理數(shù)類中,必須有一個標(biāo)準(zhǔn)化的操作,問題是,這個標(biāo)準(zhǔn)化函數(shù)是返回一個標(biāo)準(zhǔn)的有理數(shù)還是將有理數(shù)自身直接就標(biāo)準(zhǔn)化了。經(jīng)過多方面的權(quán)衡,特別是為了兼容現(xiàn)有的整型變量(int, char, short等),整型變量可看成分母為1的有理數(shù),我決定讓CRational一直處于標(biāo)準(zhǔn)化的狀態(tài)下,標(biāo)準(zhǔn)化的狀態(tài)由CRational自己來維持,客戶無須知道標(biāo)準(zhǔn)化的這個細(xì)節(jié),因此,將void standarlize()聲明于其private的區(qū)域下,其實現(xiàn)如下:
void CRational::standarlize()
{
    
if (m_nDe < 0)
    
{
        m_nDe 
= -m_nDe;
        m_nNum 
= -m_nNum;
    }

    
int nGcd = gcd(abs(m_nNum), m_nDe);
    m_nNum 
/= nGcd;
    m_nDe 
/= nGcd;
}

        其中,毫無疑問,gcd為最大公約數(shù),gcd沒有訪問CRational的任何非靜態(tài)(nonstatic)變量,因此,它必將不可成為CRational的非靜態(tài)函數(shù)。將gcd聲明為靜態(tài)函數(shù),雖然可避免污染全局空間,但也使得外部代碼必須通過CRational來調(diào)用gcd函數(shù),語法上不方便,而且,gcd本身就是一個獨立性很強而且又通用的函數(shù),從意義上,它也不應(yīng)該屬于CRational里面的東西。因此,gcd只能為全局函數(shù)。關(guān)于靜態(tài)函數(shù)和全局函數(shù)的選擇,鑒于“類的接口要盡可能的小”的原則和其他的一些問題,只要函數(shù)不訪問類的靜態(tài)變量,也不通過類的變量來訪問到其非靜態(tài)成員,它就應(yīng)該是全局函數(shù)。全局函數(shù)是好東西,某些語言為了堅持所謂的純粹的面向?qū)ο螅室獠恢С郑瑢嵲谧屓擞闷饋砗懿煌纯欤奈廴救挚臻g的問題,完全可以通過命名空間來解決。總之,只要可能的話,就應(yīng)該將函數(shù)聲明為全局函數(shù),沒什么不好。好了,請看gcd的實現(xiàn)。
unsigned int gcd(unsigned int x, unsigned int y)   
{   
    unsigned  
int  nTimes=0;   
    
for (; 0 == (x&1&& 0 == (y&1); x>>=1, y>>=1)
        
++nTimes;

    
if (x < y)
        swap(x, y);

    
while (y > 0)
    
{
        
for (; 0 == (x & 1 );x >>= 1 )
            ;   

        
if (x < y)
            swap(x, y);
        x 
-= y;
        
if (x < y)
            swap(x, y);
    }

    
return x << nTimes;
}
 
        其算法源于《編程之美》,可看成是非遞歸的版本。咦,怎么會這么長,與日常所見的到似乎不太一樣,高效算法的代碼貌似都會很長,好比strlen。再仔細(xì)看,里面居然沒有取余的操作。嗯,它的算法核心,用移位和減法這兩種快速的運算來替代取模這種慢速運算。
有了standarlize()之后,CRational的幾個函數(shù)的實現(xiàn)如下所示:
CRational::CRational(int nNumberator, int nDenominator)
: m_nNum(nNumberator), m_nDe(nDenominator)
{
    assert(nDenominator 
!= 0);
    standarlize();
}


CRational
& CRational::operator+=(const CRational& _Right)
{
    m_nNum 
= m_nNum*_Right.m_nDe + _Right.m_nNum*m_nDe;
    m_nDe 
*= _Right.m_nDe;
    standarlize();
    
return *this;
}
……
        構(gòu)造函數(shù)中,似乎應(yīng)該檢查分母為0的情況,然后拋出異常。但是,這屬于契約使用的問題,用戶違背的契約,一切后果,必須自己承擔(dān),我們的代碼無須對此負(fù)責(zé)。
        此外,就是各種+、-、*、/、==、輸出等各種全局運算符重載的操作了。得益于C++的缺省類型轉(zhuǎn)換,我們不用再做其他事情,就可以很好地讓我們的CRational很好地與融入到原有的各種整型世界中去。當(dāng)然,為了效率起見,似乎有必要針對各種整型提供+、-、*、/的各種重載版本(complex就是這樣做的),但在此,確實沒有必要。缺省類型轉(zhuǎn)換有時雖然會帶來一些問題,但是,當(dāng)確實需要它的時候,它就能發(fā)揮重大作用了。C++的各種特性就是這樣,你可以不用,它也不打擾你(你要故意或無意用錯,那也沒辦法),當(dāng)真正需要到的時候,特性的威力就顯示出來了。很多人之所以愿意沉迷于C++,就在于它不剝奪程序員的任何一點選擇的權(quán)利。
ostream& operator << (ostream& outconst CRational& rat)
{
    cout 
<< rat.Numberator();
    
if (rat.Denominator() != 1)
        cout 
<<  "/" << rat.Denominator();
    
return out;
}


CRational 
operator+(const CRational& _Left, const CRational& _Right)
{
    CRational _Tmp(_Left);
    
return _Tmp += _Right;
}
……
        嗯,一再寫這些入門級的文章,本座也很自覺有失身價,也算是對網(wǎng)絡(luò)世界的一點回報吧。只要有一個人看了,能有所啟發(fā),在下就心滿意足了,為免誤人子弟,也歡迎有人批評指正。

posted @ 2012-06-04 11:23 華夏之火 閱讀(1417) | 評論 (0)編輯 收藏

神奇的C數(shù)組

         數(shù)組在所有的語言中,以C最為簡單,就是一起始地址,外加一數(shù)組的長度,而且基本上沒有任何功能可言。然而,在所有的數(shù)組使用中,卻是C的這種簡單的數(shù)組形式,最一再使我心折,其靈活性和效率,簡直驚天地泣鬼神,前無古人,后無來者,念天地之悠悠,高處不勝寒。
首先,C數(shù)組從邏輯上講,是分形一致的(想不到其他詞來形容了),分形的意思,就是部分與整體保持一致的構(gòu)造形式,也即是數(shù)組的任何一部分也都是數(shù)組,比如一整型數(shù)組{1,2,3,4,5},連續(xù)取出其中任一連續(xù)的部分,都可以看成是一個數(shù)組,{2,3}是數(shù)組,{1,2,3}是數(shù)組,{4,5}也都是數(shù)組,差別只在于數(shù)組的起始地址和元素的個數(shù)。那怕是數(shù)組中的任何一個元素,都可以看成是一個長度為1的數(shù)組。因此,C數(shù)組的這種統(tǒng)一的格式,在作為參數(shù),傳遞給函數(shù)的時候,特別是遞歸函數(shù)中,不知道有多么方便。
         比如冒泡排序,BubbleSort(int* pInt, int nLen),用遞歸來實現(xiàn),只要冒完第1個元素之后,接著就可以遞歸,內(nèi)部調(diào)用自己,BubbleSort(pInt+1, nLen-1),繼續(xù)冒泡數(shù)組,只是數(shù)組是由第1個數(shù)之后的全部數(shù)組成的新的數(shù)組,元素個數(shù)比之前少1,一直如是處理,直到最后,數(shù)組的長度為1,于是冒泡排序完成。這種方法的成立前提,就在于C數(shù)組格式的高度統(tǒng)一。一個操作數(shù)組的函數(shù),可以操作數(shù)組的任何一部分,甚至可以操作一個變量,因為單獨的變量可以看成是元素長度為1的數(shù)組,多少次,我們用WideCharToMultiByte來轉(zhuǎn)換一個寬字符變量。C語言中操作數(shù)組的函數(shù),搭配上C數(shù)組的簡單定義,其應(yīng)用,那是相當(dāng)相當(dāng)廣泛的,并且使用起來,自然非常非常的方便,比如剛才的冒泡函數(shù)BubbleSort,只要你高興,完全可以拿來只冒數(shù)組中的隨便某一部分,這完全可以由用戶自己隨意定制,語言層面上直接支持了。
         其他語言的數(shù)組,由于特點很多,反而失去了這種邏輯意義上的統(tǒng)一處理,因為在它們那里,數(shù)組的任何一部分都不能看成數(shù)組,單獨的變量也沒法看成是數(shù)組,在它們世界,數(shù)組與獨立變量,無論如何,都沒法劃上等號。如果他們要用遞歸實現(xiàn)冒泡排序的代碼,形式上無論如何都比不上我們的C數(shù)組形式的函數(shù),誰說C的代碼就意味著代碼量多了,用得好,可以簡單得讓人贊嘆不已。
         我們再來看看C的字符串,也很簡單,它也是一個數(shù)組,只不過最后一個元素是’\nul’,加了這么一點限制之后,字符串自然就失去了數(shù)組的分形強悍,但C的字符串依然不可小看,因為字符串中,只要帶上了’\nul’,都能看成是字符串,好比,”hello”這條字符串,只要改變起始地址,就可輕而易舉地得到”ello”,”llo”,”lo”,”o”這好幾條子字符串,這個特點,可以簡化很多字符串操作,并且效率最高。此外,C字符串,只要你愿意,完成可以拿來當(dāng)成是字符數(shù)組來使用,這樣,就又恢復(fù)了數(shù)組分形功能,C函數(shù)庫中和WINDOWS API,有很多函數(shù)就是專門處理C字符數(shù)組的。

        C數(shù)組的這種分形特性,在STL被抽象成迭代器,于是,在C++中,就失去了原有的輝煌。但是,在很多簡單的設(shè)計中,依然有著不可低估的力量。

        C的很多東西,就是這樣,因為簡單,所以強大得令人心寒。函數(shù)、結(jié)構(gòu)體、數(shù)組、GOTO、枚舉這些簡單的東西,巧妙的配合使用,可以玩出很多很多意想不到的神奇功能出來,令人擊節(jié)贊嘆,而且不會像C++那樣,存在著所謂的什么心智負(fù)擔(dān)。此外,C中的宏,嘿嘿,俺就不提了,變化多端,鬼神莫測。對于C,我越來越敬畏,它遠(yuǎn)遠(yuǎn)不似表面上看的那么簡單,其背后自然潛藏著一套精神規(guī)則。即使看到每一行C代碼,內(nèi)心都能知道它的相應(yīng)的匯編代碼,那又怎么樣,它總是有辦法讓你驚喜不已。

posted @ 2012-06-01 16:32 華夏之火 閱讀(2258) | 評論 (12)編輯 收藏

類設(shè)計一則,GDI對象選入器

        雖然很痛恨MFC,但還是要經(jīng)常使用MFC開發(fā)界面,雖然MFC怎么怎么的不好,但還是可以應(yīng)付一般的界面要求,而且其運行效率也可以接受,關(guān)于這一點,它在WINDOWS3.1之時,就已經(jīng)能勝任了,并且在下現(xiàn)在使用MFC,基本上也能隨心所欲了,想怎么整就怎么整。為了減少以后浪費在MFC上的時間,我決定重構(gòu)一套著名的MFC上的界面庫。界面庫這個東西,大家都知道,其各種美觀的界面,基本上都是一筆一筆畫出來的,代碼中大量地使用了DC的各種操作,不可避免,就一再出現(xiàn)了好比以下類似的代碼:

    HPEN pen = CreatePen(……);
    HPEN oldPen 
= (HPEN)SelectObject(hDC, oldPen);
    SelectObject(hDC, pen);
      ........
    DeleteObject(pen);

 

 

        這些代碼,既無味,寫起來又容易出錯,它存在3個很明顯的問題:1SelectObject使用了類型轉(zhuǎn)換,一不小心,自然就轉(zhuǎn)換錯了,MFC中通過對象指針,內(nèi)部做了轉(zhuǎn)換,避免了這個問題;2、要將舊有的GDI對象選回DC中,很容易就遺忘了這一步代碼,并且如果要同時操作幾個DC的時候(這種情況較少見),在選回舊GDI對象時,那就更容易出錯了;3、還要刪除對象,這個,MFC中通過GDI對象的構(gòu)造函數(shù)來消除這個問題。這樣也罷了,但還有一個問題最讓人不能忍受的,就是,代碼中要定義一個舊的GDI對象變量,以最后選回設(shè)備環(huán)境之中,因為我最討厭定義新變量,新函數(shù)了,每次都要變量名和函數(shù)名琢磨半天,當(dāng)然,最討厭的還是編寫重復(fù)的代碼了。于是,我決定編寫一個類,以免總是要寫這些毫無新意的重復(fù)代碼,希望這個類是這樣使用的:

CXGdiObjectSelector selector(hDC);
selector.CreatePen(……);
selector.SelectBrush(brush);
  ........

        不需要定義舊的GDI對象變量,畫圖完成之后,也不需要選回設(shè)備環(huán)境之中了,也不需要手工刪除之前創(chuàng)建的GDI對象,一切,這個類都幫你代勞了,咦,這么神奇,它是如何做到的,自然是析構(gòu)函數(shù),再次向偉大的析構(gòu)函數(shù)致以最高敬意。這個類,比之于剛開始的手工打造,它需要執(zhí)行構(gòu)造函數(shù),以保存hDC到內(nèi)部成員變量,很明顯,多了一步賦值操作,此外,可以預(yù)料,這個類里面應(yīng)該還有其他的初始化操作,這又是不可避免的多余代碼。當(dāng)然,這里的多余,都是我能接受的,我也深知,既要馬兒,又要馬兒不吃草,那是不可能的神話。但是,在實現(xiàn)這個類的時候,我想了種種辦法,包括模板元編程也祭上了,始終還是存在其他多余的操作,再次審視剛剛開始的一段代碼,不得不承認(rèn),它丑是丑了點,但執(zhí)行的效率確實真他媽的高,而且所占的空間也很少,全部沒有一丁點多余之處。好了,進(jìn)入我們的類的實現(xiàn)。

class CXGdiObjectSelector
{
public:
    CXGdiObjectSelector(HDC hDC)
    
{
        ASSERT(hDC 
!= NULL);
        m_hDC 
= hDC;
        m_nSelectedFlags 
= 0;
        m_nCreatedFlags 
= 0;
    }


    
~CXGdiObjectSelector();

    
void SelectPen(HPEN pen)
    
{
        replaceObject(pen, XFLAG_PEN);
    }


    
void SelectBrush(HPEN brush);
      ........

    
bool CreatePen()
    
{
        HPEN hPen 
= CreatePen();
        
if (hPen == NULL)
            
return false;
        replaceObject(hPen, XFLAG_PEN);
        m_nCreatedFlags 
|= XFLAG_PEN;
        
return true;
    }

      ........

private:
    CXGdiObjectSelector(
const CXGdiObjectSelector&);
    
void operator = CXGdiObjectSelector(const CXGdiObjectSelector&);
    
enum {__nGDI_SIZE = 5};
    
enum {XFLAG_PEN=1, XFLAG_BRUSH=2, XFLAG_FONT=4, XFLAG_BITMAP=8, XFLAG_REGION=16};
    HDC  m_hDC;     
    HGDIOBJ m_hOldGdis[__nGDI_SIZE];
    WORD m_nSelectedFlags;
    WORD m_nCreatedFlags;
}
;

        整個類的定義的還是很直觀。只是那一組創(chuàng)建GDI對象的成員函數(shù),顯得有點格格不入,根據(jù)單一職責(zé)原則,實在不應(yīng)該加入這些東西,但是,加入這些操作,確實會給使用的時候帶來極大的方便。至于禁用了拷貝和賦值函數(shù),感覺有點多此一舉,但是為了滿足某些C++潔癖者的強迫癥,我還是做了妥協(xié)。這個類其他代碼的實現(xiàn),相當(dāng)簡單,我就不贅述了。

        公道自在人心,這個類在選入選出GDI對象,毫無疑問,確實方便安全,但是它以犧牲執(zhí)行效率和空間為代價。代碼編寫,不外乎是在做各種各樣的權(quán)衡,有時付出類型安全,以換取更大的靈活性;有時又以付出靈活性,以換取類型的安全;有時以通用性換取效率;有時又要以效率換取通用。不能簡單地說這種權(quán)衡好不好,只能說更加合適而已,在某一處上,比較強調(diào)什么,就以犧牲其他的特性來得到,謹(jǐn)記80%20%的原則。C++的深入人心,在于它不剝奪程序員選擇的權(quán)利,同時它又提供了豐富的特性,以供你做各種各樣的交換。通用、靈活、效率、安全,這四者總是不可協(xié)調(diào)的矛盾。MFC框架的最大錯誤就在于:犧牲了很大很大的靈活、效率、通用,最后只獲得了一點點類型安全,這無疑是失敗的交換。

posted @ 2012-06-01 10:56 華夏之火 閱讀(1253) | 評論 (5)編輯 收藏

WINDOWS與設(shè)計模式

        作為C++的堅實粉絲,我一直很排斥JAVA,并不是JAVA這種語言不好,而是Java迷的那副嘴臉,事事都要與C++爭,并且還堅稱JAVA比C++,甚至連執(zhí)行效率都要勝過C++,什么JIT運行時能監(jiān)視代碼,選取執(zhí)行頻率最高的代碼,根據(jù)特定的平臺,進(jìn)行特別處理,生產(chǎn)出最優(yōu)化的機器代碼;又說什么垃圾回收能夠解決C++中的內(nèi)存管理問題,并且內(nèi)存分配遠(yuǎn)遠(yuǎn)勝過C++中的手工人肉管理內(nèi)存的毛病,其實,內(nèi)存管理從來就不是C++中的嚴(yán)重問題,只要設(shè)計得好,應(yīng)用層中的代碼甚少可以不出現(xiàn)new, delete。至少delete可以不出現(xiàn),大家知道WHY的;種種論調(diào),無理取鬧之極。而最令我受不了的,就是他們閉口開口,都離不開設(shè)計模式,搞得設(shè)計模式好像成為了JAVA的專有產(chǎn)品。須知,第一本設(shè)計模式的書,也是最最經(jīng)典的那本四人書,其對設(shè)計模式的實現(xiàn)語言,采用的就是SmallTalk 和C++,一開始就不關(guān)JAVA的一點事情,并且書中C++還占了較大的比重。關(guān)于四人書,最讓我欣慰的一件事情就是,四位巨頭并沒有響應(yīng)JAVA迷的強烈呼聲,采用JAVA來實現(xiàn)設(shè)計模式的第二版。當(dāng)然,我也承認(rèn),用JAVA來實現(xiàn)設(shè)計模式,確實來得要比C++清爽,JAVA的這種語言,好像就是專為設(shè)計模式量身訂做。只可惜,市面上任何一本JAVA的設(shè)計模式書,沒有一本及得上我們C++的那一本設(shè)計模式圣經(jīng),C++中不必再需要設(shè)計模式的書了,因為最好的書就已經(jīng)擺在那里了,涵蓋了設(shè)計模式中的方方面面,濃縮精華得很。突然想起,C++的教材也不需要那么多,因為老爺子已經(jīng)寫了一本最好的書了,其他書的內(nèi)容,都已經(jīng)涵蓋在那本C++語言圣經(jīng)中了。至于那些不被C++圣經(jīng)所提及的,那都是一些走火入魔的玩意,玩玩還可以,開闊開闊視野也不錯,但真要用在實際項目中,還是少用為妙。罪過罪過,C++中的好書確實有好幾本,不宜一棍子打死。
        有趣的是,設(shè)計模式在JAVA中被捧上了天,沒有設(shè)計模式,JAVA就沒法活下去。反而C++作為設(shè)計模式的第一實現(xiàn)語言,卻不怎么感冒。不能說被輕視,但起碼沒有普遍被重視。即使C++中的高手,也沒有對設(shè)計模式如何如何的推崇備至,他們貌似更加喜歡玩語法糖,熱衷于在C++中模擬出其他語言的特性。這也難怪,C++有四種編程典范,設(shè)計模式的主要應(yīng)用就在于面向?qū)ο螅嫦驅(qū)ο蟛贿^是其中最不成熟的一種,在C++中,最成熟的要數(shù)基于對象的編程,當(dāng)然,拜C語言成熟,面向過程也熟得要爛了,泛型這東西也挺前衛(wèi)的,而C++中的面向?qū)ο螅恢背裘阎瑹o論怎么努力,都難以搬上臺面。注意,C++中的面向?qū)ο笈c虛函數(shù)是兩碼事,至少在我看來是這樣的。
        好了,該談?wù)勎覍υO(shè)計模式的看法了,一句話,我對設(shè)計模式殊無好感,源于我內(nèi)心對當(dāng)前的面向?qū)ο缶幊田L(fēng)格的排斥,因為那是偽面向?qū)ο缶幊蹋^的偽面向?qū)ο螅褪侵竿ㄟ^單根繼承和針對接口或抽象類來寫代碼,就以為是在進(jìn)行面向?qū)ο蟮木幊蹋菍嵲谑翘煺媪恕TO(shè)計模式中的那些點子,用得好,確實能解決偽面向?qū)ο蟮囊恍﹩栴},無可厚非;用得不好,無緣無故地引入一些不必要的東西,模式的應(yīng)用意味著間接性的增厚。現(xiàn)實中,能恰當(dāng)?shù)赜煤媚J降娜耍僦稚伲J娇偸浅霈F(xiàn)在一些不必要出現(xiàn)的場合下。很多人都是生吞活剝了23種模式之下,內(nèi)心就沾沾自喜,到處躍躍欲試,鮮有人嘗試?yán)斫饽J奖澈蟮慕y(tǒng)一思想是什么,或者說,如果代碼本身就已經(jīng)能夠很好類與類之間的耦合問題,可勝過千萬種設(shè)計模式。
        以下文字,與孟巖大俠的觀點,存在部分重復(fù)之處,讀者認(rèn)為在下是在拾他的牙慧,我也不反對,畢竟人家的文章反表在前,我無話可說,本文的重點,旨在表達(dá)在下對設(shè)計模式的鄙視。
        既然有偽面向?qū)ο螅陀姓婷嫦驅(qū)ο蟆U婷嫦驅(qū)ο蟮木幊蹋褪悄阒恢酪粋€對象的ID,其他的一切事情,它繼承自那些父類,實現(xiàn)了那些接口,統(tǒng)統(tǒng)一概都不得而知,然后你只能通過這個ID給對象發(fā)送消息,以此來驅(qū)使對象做一些事情,注意,確確實實是只是發(fā)送消息,而不是調(diào)用該對象的函數(shù),那怕是調(diào)用了該對象實現(xiàn)的接口函數(shù),也意味著該對象的依賴,好吧,說錯了,是對該接口的依賴,不管怎么樣,都是依賴,而且依賴接口,還搞得客戶代碼和對象都要依賴于同一個接口了。
給對象發(fā)送消息,聽起來似乎有點抽象,但是,只要聯(lián)想到WINDOWS的窗口句柄和消息處理函數(shù),就自然明白是怎么回事了。在WINDOWS下,每一個窗口都是一個對象,窗口句柄代表了對象ID。操作窗口時,只能通過SendMessage或PostMessage來讓窗口作一些事情。SendMessage或PostMessage的四個參數(shù),分別是對象ID、消息編號、消息參數(shù)1和消息參數(shù)2。客戶代碼在使用窗口時,不需要假設(shè)什么接口,能做的,僅須做的,僅僅就是給它發(fā)送消息,客戶代碼完全無須依賴于什么鬼接口,非常簡單明了。但是,這里存在一個問題,消息參數(shù)1和消息參數(shù)2都是void*類型,不安全耶,要是誤發(fā)送錯了消息,那該怎么辦。在偽面向?qū)ο笾校蛻粢部赡茉谡{(diào)用接口參數(shù)時,也可能會傳錯了參數(shù),只不過是編譯器可以幫你找出來。其實類型不安全,也沒什么,客戶既然要發(fā)送消息給對象,自然要遵守使用的協(xié)議,自然要清楚自己在做什么事情,這本來就是C語言的精神,一切相信程序員。
        好了,該是給WINDOWS大唱贊歌了。WINDOWS系統(tǒng)的窗口是我見過中算是比較象樣的面向?qū)ο蟮牡浞读恕⒚嫦驅(qū)ο蟮木褙瀼氐降祝翱趯ο蠛芎玫刈龅搅藘H僅是純粹解析消息,執(zhí)行消息,與外界的其他對象,不存在任何一點耦合。一個窗口對象中的lpfnWndProc其實可以理解成指向虛函數(shù)表的指針,但是它卻要比虛函數(shù)表的指針功能更加強大靈活,并且還不存在虛擬函數(shù)表的種種問題。強大之處,就之于lpfnWndProc相當(dāng)于指向一個龐大的虛函數(shù)表,這個表有2的32次方多個虛函數(shù),靈活之處可以突破虛函數(shù)的種種限制。當(dāng)你要給一個窗口對象添加新的功能,或者不滿意它的某些原有操作的時候,你完全可以子類化該窗口,在子類化操作中,截取關(guān)心的消息,解析消息,執(zhí)行消息,至于其他的消息,直接交給lpfnOldWndProc或DefWindowProc就是了。從這個意義上講,所有的窗口對象繼承于DefWindowProc,而子類化窗口即是繼承于lpfnOldWndProc,但是,這里的繼承,卻沒有偽面向?qū)ο笾械睦^承或?qū)崿F(xiàn)接口的種種弊端。而且,在你子類化窗口的時候,不用影響到客戶的任何一點點代碼,客戶代碼依舊是發(fā)送消息,卻不知道窗口對象已經(jīng)舊貌換新顏了,OH,這實在太美妙了。所有的耦合,奇跡般的消失得干干凈凈了。消息比之于接口,那完完全全是純粹的正交關(guān)系,并且沒有接口的種種累贅,種種缺陷。并且更爽的是,即使對象沒有處理消息,也沒有關(guān)系,客戶代碼依然可以給對象發(fā)送消息。
        從某種意義上講,設(shè)計模式不過是為了解耦對象與對象之間的耦合關(guān)系,當(dāng)對象之間不存在耦合的時候,設(shè)計模式還有什么存在的意義嗎?以下結(jié)合WINDOWS系統(tǒng)來理解,所謂的設(shè)計模式,不過是消息發(fā)送的一些應(yīng)用罷了。下面的舉例,例子之間沒有什么必然關(guān)系,我想到那里就寫到什么,模式后面似乎應(yīng)該帶上英文,但我也懶得寫了。

觀察者模式:一個廣播消息就搞定了;
模板方法:不過是按順序給一個對象發(fā)送某些指定的消息而已;
工廠方法、抽象工廠:用一個或幾個lpClassName就搞定了;
原型:不過是發(fā)送一條WM_COPYOBJECT的消息而已;
裝飾者或者狀態(tài):嘿,子類化就完成了,并且非常徹底,一點都不覺得別扭;
命令:對象將SendMessage中的消息編號、消息參數(shù)1和消息參數(shù)2保存起來就是了,這沒什么大不了的;
策略:不過一個回調(diào)函數(shù),外加必要的參數(shù);
橋接:貌似沒什么必要;
……
沒有了!落得個一片白茫茫大地真干凈!

posted @ 2012-05-31 17:59 華夏之火 閱讀(2261) | 評論 (8)編輯 收藏

試論C++類庫開發(fā)之難

         很久很久以前,世界上曾經(jīng)存在著這么一種語言,用它寫出來的代碼,既能保證高效的運行效率,又兼具優(yōu)雅十足的美感,并且,所有語言存在著的不足,都可以通過添加新的自定義的類型來實現(xiàn)出來,并且,新的類型,只要做得好,可以與原有的類型無縫交互。在它的世界里面,那是一切皆有可能。并且,它還承諾,雖然它的功能非常豐富,但各個功能特性都可以和睦相處,而且,你代碼中不必要用到某項功能時,你完全不必為這項功能付出那怕一丁點的代價,如果這個語言愿意作一點點妥協(xié),可能它就不是現(xiàn)在的這個樣子,至少將更加容易使用。所有它承諾的一切,乍聽起來美妙無比,而且,它也似乎做到了,真的做到了馬兒既能跑,又能吃盡量少的草,前提是開發(fā)人員要小心翼翼地用好這門語言的一切特性。只可惜事與愿違,這門語言確實看起來,真的可以創(chuàng)造出完美的東西,但事實上,沒有人能用它寫出過一個好東西,那怕是簡簡單單的字符串,要做得大多數(shù)人拍手叫好,也是千難萬難,完美的字符串,從來就沒有出現(xiàn)過。各位同學(xué),自然知道在下說的是什么語言,等千呼萬喚的C++1X始出來之后, 真不知道還有什么東西,是C++沒法做的,要在C++中增加幾種新的編程模式,也不會有太多的困難。起碼,函數(shù)式的編程,使函數(shù)好像成為一等公民,絕不是什么難事。
          C++本身自然不會有什么問題,說它復(fù)雜吧,它確實復(fù)雜,但是完全可以無視,異常不好,禁用就是;多繼承很壞,不用就是;模板過于復(fù)雜,忘了它吧;……,起碼它做到了,你不喜歡的特性,你可以不必付出任何代價。但是,成千上萬的C++高手大牛巨俠,這么多年下來,卻沒有搞出幾個似模似樣的類庫出來,除了STL(其實STL也有一些問題),難道是程序員錯了,難道是他們?nèi)e了嗎?可是,C、JAVA、PYTHON它們,怎么就不存在這些問題呢,里頭的好東西還真不少,起碼在它們的圈子里面,碼農(nóng)們都覺得類庫沒問題,很好,用起來很放心,可以放心的使用。又或者是,C++碼農(nóng)對自家的類庫要求過高了,他們的眼光太挑剔了,大家人人都有多疑癥,但是,一旦他們使用其他的語言,又很樂意使用其他語言所提供的類庫或者是框架,怎么就不會那樣疑神疑鬼,沒有各種并發(fā)癥,起碼在下是這樣的,你要說C++沒問題,那鬼才愿意相信呢?那么,假如C++有問題,問題又是那里呢?
          我以為,C++的問題,就在于它似乎很鼓勵碼農(nóng)制造輪子,撰寫類庫,然后對所寫的類庫貌似沒有過多的要求,但實際上,C++對類庫的要求,是所有語言中最高的。好比,要你寫的軟件,需求很不明確,貌似用戶說,你隨便寫,然后它能完成任務(wù)就可以了,但實際上這樣的軟件最難開發(fā)了。知道嗎?C++的類庫,有什么樣的要求:1、運行的效率要高,處理一切細(xì)節(jié),高效源于對細(xì)節(jié)的特定處理;2、表現(xiàn)出來的接口和語義要無限接近原始內(nèi)嵌的類型,值語義,吖的,這要求也太高了。3、要有足夠的通用性,滿足抽象的需要。這三條的任何一條都不是什么省油的燈,直接可以K死秒殺多少C++高手的幾千萬只脆弱的腦細(xì)胞。這三條下來,本身就差不多是一個難以完成的事情,但是,這還不是最要命。最能搞死人的,是C++又提供了太多的豐富特性,什么模板、操作符重載、異常、多繼承、內(nèi)聯(lián)、……,心理學(xué)的一項研究表明,當(dāng)選擇太多之時,當(dāng)事人就會分散精力,不知道如何選擇。用C++開發(fā)類庫,眼前存在著無限種選擇,貌似條條大路通羅馬,但實際上,卻是條條大路是死路。但這也沒什么,最后一擊,如果沒有必要,鬼才愿意費心費神寫類庫,關(guān)鍵在于C++只是提供實現(xiàn)高標(biāo)準(zhǔn)類庫的基本平臺而已,語言本身就沒有一點點能用的實在的東西,那怕一個簡單的字符串類,一切都必須通過類庫來完成,它一開始一直都在鼓勵碼農(nóng)寫類庫,試問普天之下,有那個C++碼迷,能夠抑制造輪子的誘惑,于是,悲劇就誕生了。
          反觀其他的語言,為什么就不存在這些問題呢?
          在C里面,是絕對不會出現(xiàn)C++的類庫困境,首先,它只有一種選擇讓你開發(fā)類庫,那就是函數(shù)和函數(shù)指針,外加結(jié)構(gòu)體、數(shù)組,或者,必要的時候,使用上點宏,嗯,就只有這么多。你要用就用,不用就拉倒,在這么有限的條件,你只能做出最簡單但卻又是最精妙的設(shè)計,都能直達(dá)問題的本質(zhì)。基本上,C的設(shè)計,不會存在一點點多余的抽象層次,一拳一拳,都打在要害上,針針見血。更何況,C的要求也不高,除了效率之外,就是要保持函數(shù)接口語義的清晰,而這并不難做到。
          那么,在高級語言中,又是另一番天地了。當(dāng)然,它們也提供了多數(shù)C++的豐富特性,你自然有很多選擇,但是,這些語義卻屏蔽了很多底層的細(xì)節(jié),并且提供了豐富的語言平臺特性,什么反射啊、豐富的API框架,單是垃圾回收和單繼承,就功德無量,足以拯救多少弱小脆弱的靈魂。是的,人家效率確實不太高,也沒有要求值語義,但這不要緊,因為人家語言一開始就沒有這樣的效率標(biāo)榜要求,它們一上來就讓碼農(nóng)直面慘淡的應(yīng)用,進(jìn)入問題領(lǐng)域,不需要撰寫什么高通用性的類,因為平臺本身就提供了,你要寫的類,都是問題領(lǐng)域中的東西。這實在能解救多少腦細(xì)胞啊,減少多少不必要的工作量啊呢。
          沒有必要的類庫來支持,C++做開發(fā),工作量相當(dāng)浩大,并且還容易生產(chǎn)出一堆不成熟的通用類;但是,必要的成熟的類庫,市面上又沒有多少個,目前的所謂的高級的C++類庫,大多數(shù)都在為了C++而C++,你們知道我在說的是那一個的。當(dāng)然,俺是沒能力寫出什么類庫的,所謂的眼高手低,大概就是這樣吧。
 或者,痛并快樂著,是C++碼迷的持久不退的熱情所在。只是,人生何其寶貴,將它們浪費在C++上,實在有點不值得。當(dāng)然,活著只要快樂就好,C++也確實能給人帶來很多快樂。

posted @ 2012-05-30 16:53 華夏之火 閱讀(4588) | 評論 (31)編輯 收藏

MFC,一開始就錯了

        謹(jǐn)以此文祭奠過去多少年下來,日日夜夜學(xué)習(xí)MFC源代碼的寶貴時光。

        曾經(jīng)以為MFC是世界上最神圣無比的東西,發(fā)誓在沒徹底弄透MFC之前,絕不越雷池半步,碰都不要去碰LISP、ERLANG、PYTHON、VCL、ATL、STL、LOKI、BOOST各種美妙的好東西。即使一再聽到高人說MFC設(shè)計得不好,非常糟糕,也都至死而不悔,然后只要稍微聽到有人在說MFC的偉大,心里就特別高興。買了各種各樣的VC方面的書,什么四大天王、什么幾十百千例、什么VC項目實例,徹夜苦讀,單步調(diào)試,一次又一次地徘徊在MFC套來套去的各種美妙代碼之中,觀察里面各個對象的值。一次又一次地感嘆MFC的偉大,一直在想,這輩子,如果能徹底地掌握MFC里面的一切秘密,朕就心滿意足了。終于有一天,終于徹底醒悟,原來MFC真不是好東西,不過就是那些伎倆而已,真不該將它看得如此的偉大。這種念頭一起來,再回過頭來看MFC的一坨一坨代碼,只覺得清晰無比,又感覺代碼之中充滿了各種無奈。接著再看看那些VC方面的書,除了四大天王還稍微有些可取之處,其他的任何一本,奇臭無比,沒有一行代碼經(jīng)得起推敲, 文筆也糟糕無比,其內(nèi)容又一再重復(fù),抄來抄去,真正是一本又一本的狗屎。 感嘆一下:這些書犧牲了多少寶貴的樹木,又折磨死了多少讀者的腦細(xì)胞,實在罪大惡極。

        在MFC大河日下的當(dāng)今的大環(huán)境之下,在下也不想對它落井添石,畢竟也在其上學(xué)到不少好東西,特別是閱讀代碼的能力,試問這么糟糕復(fù)雜的框架都可以精通,天下還有什么代碼能看不懂(別自信滿滿,你敢碰BOOST里面的那些奇技淫巧,匪夷所思的幻碼嗎)。但實在忍受不了軀體內(nèi)另一個人日日夜夜,旦夕不停的訴求,兼之又一直恨鐵不成鋼,又為自己失去的青春陣痛不已。而VC2008中BCG的出現(xiàn)之后,VC2010還依然保留著那些蠢笨無比的BCG文件,不思悔改,終于讓某徹底對MFC死心了,這貨沒得救了。

        好吧,廢話少說,趕緊進(jìn)入正題。

        國內(nèi)C++界中一大俠說過,任何東西,都要先抓住其主干,其他的一切細(xì)節(jié),都可以留待以后慢慢補充,否則,一開始就陷入細(xì)節(jié),想跳出來就不容易了。大俠此語,實在有道理。(不知為什么,此大俠后期一直在對C++表示不滿,當(dāng)然,人家現(xiàn)在的境界,豈是我等小民所能體會)。不說其他,就說在C++中,必須站在一個高度上,縱觀全局,才能將一切看得通通透透。于是,下面就嘗試爬上高峰,來俯瞰MFC中的眾生。MFC的主干,在下看來,既不是什么六大關(guān)鍵技術(shù),也不是什么文檔視圖結(jié)構(gòu),更不是COM的封裝,而是為了將原始的API中的各種對象,好比窗口、GDI、設(shè)備環(huán)境等,都映射成相應(yīng)的多線程安全的C++對象,好比將HWND搞成CWnd,以期減輕WINDOWS下界面開發(fā)的工作量。

        這種想法,其實也無可厚非,如果真能封裝得好,確實可以提高生產(chǎn)代碼效率,但是從C++和WINDOWS的設(shè)計觀念中來看,這種映射,還是存在很大的問題,稍后再述。問題在于,MFC的實現(xiàn),異常丑陋呆板,通過幾個全局的線程安全局部變量的映射表,將句柄(Handle,此詞譯得相當(dāng)令人惡心)與其相應(yīng)的C++對象指針形成一一對應(yīng),并且指定,在大部分的消息處理和參數(shù)傳遞中,所有的參數(shù)由原本的句柄一律由對象指針來代替,然后就高高興興地宣稱已經(jīng)完成任務(wù)了,這實在讓人哭笑不得。不說別的,即使是消息處理,MFC即使已經(jīng)做了很大的努力,也都無法將句柄參數(shù)搞成對應(yīng)的對象指針,單是這一點,就已經(jīng)失敗了。然后再看看,它最后封裝的做法,不過是將API函數(shù)中的第一個參數(shù)為句柄的都改成相應(yīng)的C++對象的函數(shù)成員,好比將SetWindowText搞成CWnd::SetWindowText,美其名曰,薄薄的封裝。這真是要笑掉大牙了,既然是這樣的薄薄的封裝,那還干脆不如不封裝了,我想破了頭,也看不出這樣的封裝,帶來了什么樣的好處。反而將原本全局函數(shù)的各種靈活性,都給葬送在類成員函數(shù)這個監(jiān)獄中。全局函數(shù)的各種好處,除了是全局這個缺陷之外,實在都是類成員函數(shù)沒法比的。而且,鑒于API中數(shù)量實在太多,于是搞出來的C++對象都是龐然大物,最臭名昭著的,當(dāng)然要數(shù)CWnd和CDC這對黑風(fēng)雙煞了,使用起來,極不方便,極難學(xué)習(xí)。一個類的函數(shù)過多,無論如何,都是失敗的設(shè)計。還有,如果WINDOWS系統(tǒng)新增了什么使用到句柄的函數(shù),這些類也都要相應(yīng)地增加新成員函數(shù),再進(jìn)一步說,如果用戶自己因為需要,也要開發(fā)使用到句柄的全局函數(shù),要怎么想辦法整成員函數(shù)呢。其實,MFC中的所謂的線程安全,其實也不過是自欺欺人的說法而已,你要想在MFC中多線程的使用窗口對象,即使不是不可能,那也要花費九牛二力氣才能在多線程中共享窗口對象,一切皆只因MFC已經(jīng)很努力地做到了不讓你在多線程下使用窗口對象。

        好了,再進(jìn)一步考察,在這種思路的指導(dǎo)下,最后MFC被設(shè)計成一個什么樣的框架。一直很懷疑想,MFC的設(shè)計者,是否不是對WINDOWS API的精神了解得不透徹,又或者對C++不是很精通,否則,無論如何,斷斷都不會整出MFC這樣的一個人不象人、鬼不似鬼,精神分裂、做事虎頭蛇尾的鬼東西出來。要不然,我們用MFC開發(fā),就不會覺得束手束腳,很不好施展拳腳,一再感覺一直受制于其可惡的呆板的框架之下,這并不是說框架不好,只是MFC這套框架,實在很不好擴充。這樣也罷了,問題是,MFC在使用上,又很不人性化,你又要陷入各種各樣的細(xì)節(jié)之中,什么二段構(gòu)造,有些對象又要自己刪除,有些又不能自己刪除等,更要命的是,MFC的運行效率又臭名昭著。有些同學(xué)就會問,貌似在MFC開發(fā),其實也很強大很靈活,殊不知這種強大和靈活,那都是源于C++的強大靈活,也源于WINDOWS系統(tǒng)的強大靈活,源于MFC只對API只作了一層薄薄的封裝,與MFC本身的設(shè)計沒什么太多的關(guān)聯(lián)。當(dāng)拋開MFC,直接用API和C++來寫代碼,就體會到什么才叫做強大靈活,除了代碼比MFC多一點之外,一切都要比MFC來得清爽,也要來得容易維護(hù)。如果,如果設(shè)計做得好,可能總體的代碼量不會比用MFC的多也說不定。

        于是,這么一個偉大的框架,表現(xiàn)出來的,很多地方都C++的精神格格不入。承然,MFC剛開始設(shè)計時,缺乏太多的C++的特性的支持,而不得不另起爐灶。但是,就算沒有C++98中的功能支持,MFC也不該出現(xiàn)以下這么重大的設(shè)計問題。
1、 不需要用到的特性,我們依然要付出種種代價。我們看到CCmdTarget集成了COM的功能,CWnd中對ActiveX的支持,就算我們的代碼不包含一丁點使用到COM的代碼,我們都要背負(fù)每個CWnd中將近100個字節(jié)的大小,運行過程中一再要忍受框架代碼中對ActiveX變量的一次又一次地檢查。我們看到,就算不使用文檔視圖結(jié)構(gòu),CMainFrame中都要承受對它的支持代碼,和各個不必要的成員變量。總之,MFC中有很多特性,就算不愿意使用,它們還是在那里,不容許我們忽視。
2、 耦合太厲害。我們看到,CWnd中居然出現(xiàn)了對子類CView和CFrameWnd的引用,CControlBar的成員函數(shù)中有對CToolBar的CAST,然后,CFrameWnd又引用到CControlBar和CView,CControlBar和CView也都用到CFrameWnd的函數(shù),各種各樣好厲害的循環(huán)依賴,這么糟糕的設(shè)計竟然出現(xiàn)在C++的著名的界面框架,不得不令人嘆息不已。這樣的耦合,就是在MFC之中,一個類設(shè)計得再好,也只能在某些特定的場合下使用。好比, CControlBar, CSplitterWnd這兩個好東西只能活在CFrameWnd,CView的環(huán)境下,想在其他的地方使用上就煞不容易了。就算是著名的文檔視圖結(jié)構(gòu),也都是問題很大,如果我的類,想要訂閱文檔中的通知消息,還要求必須派生于CView中,這個限制也太厲害了,于是為了它這個死板的設(shè)計,有多少次,我們必須改變設(shè)計,以適應(yīng)MFC的無理取鬧的要求。
3、 功能太過單薄:MFC僅僅是作為一個界面的基本框架而已,僅僅實現(xiàn)了從句柄到對象的映射,然后,再加上一點點所謂的文檔視圖的MVC模式,必要的多視圖多子窗口的支持,基本的必要的COM支持,對API的薄薄封裝,僅此而已(至于SOCKET、WININET和數(shù)據(jù)庫的MFC類,又有多少可利用的價值)。有多少次,我們想對對話框進(jìn)行拉伸的操作;有多少次,我們需要用到表格控件;……,但是,這一切,想要MFC提供更加強大的控件,完全都沒有。必須求助于第三方控件公司庫,好比,BCG、XTREME,你們知道,MFC的代碼就算再爛,也還是很有分寸,還能保住最基本的底線,但是,第三方控件的代碼,那真的是,讓人大驚失色,沒有做不到的,只有想不到的。要知道,C++可是很強大的,但是,用它做出來的框架,卻如此的功能之小。
4、 MFC中的類的功能不單一,承載了太多的責(zé)任,導(dǎo)致很容易就出現(xiàn)了很多重復(fù)的代碼:好比CCmdTarget中原本只是為了可以處理消息,后來居然還兼具COM的功能。又好比,CList, CMapPtrToPtr里面又都包含了內(nèi)存池的實現(xiàn)代碼,原本這些內(nèi)存池的實現(xiàn)代碼就應(yīng)該剝離出來。
5、 行為錯亂,神經(jīng)分裂,沒有統(tǒng)一的接口,以至于使用起來,極易出錯。很多地方,都努力地去做了,但是都做得不徹底。企圖封裝所有的WINDWOWS的界面的API,但無論如何,都做不到,而且它自己內(nèi)部也都知道沒辦法做到,但它還要努力地去做。
……

        各種各樣的問題,難怪MFC的書那么多,沒有一本可以作為面向?qū)ο蟮牡浞秾W(xué)習(xí),即使四大天王,也不過只是沒有那么惡臭而已。原來一切,皆只因為源頭本就臟了。之前還以為,MFC,還能算是一積心堆砌而成垃圾。總體設(shè)計,雖然確實糟糕透頂,但細(xì)微上看,還能做到讓人擊節(jié)贊嘆,好比對于線程局部變量的封裝擴展,消息映射表。可是,即使局部代碼的實現(xiàn),也都充滿了重重的缺陷。

        如果說MFC還有些可圈可點之處,VC2008之后引進(jìn)的BCG的界面補丁,完全就將這些可稍為搬上臺面的東西直接給淹沒掉。BCG的出現(xiàn),對MFC而言,根本就不是狗尾續(xù)貂,而是狗尾之上再綁上一只蠢笨無比的大豬。直接就將MFC推入萬劫不復(fù)之地。

        MFC,OH SHIT,你可以去死了!

        突然驚覺,C++的類庫框架,無不充滿了各種各樣的缺陷,除了STL還差強人意之外(其實也問題多多),沒有一個能夠讓人看著滿意,用著放心。于是,我要投入LISP和C的懷抱之中了,不,一定要頂住,C++是我的最愛。

posted @ 2012-05-30 14:15 華夏之火 閱讀(19956) | 評論 (47)編輯 收藏

C++代碼(4)排列與組合

     摘要:         書接上回,繼全排列之后,接下來的任務(wù)自然是要給我們的Permutation添加上部分排列的功能。所謂的部分排列,為了便于行文的展開,我也不怕再舉例,比如,針對0到4這5個數(shù)的P(3,5),其排列如下:012,013,014,021,023,024,……,234,240,241,…...  閱讀全文

posted @ 2011-07-19 19:04 華夏之火 閱讀(3149) | 評論 (5)編輯 收藏

僅列出標(biāo)題
共5頁: 1 2 3 4 5 

導(dǎo)航

統(tǒng)計

常用鏈接

留言簿(6)

隨筆分類

隨筆檔案

搜索

積分與排名

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧美日产图| 久久久蜜桃一区二区人| 亚洲福利在线视频| 久久成人免费| 亚洲国产成人午夜在线一区| 久久久久久久久蜜桃| 久久成人18免费网站| 极品日韩av| 欧美r片在线| 欧美大胆成人| 午夜精品免费| 久久精品国产视频| 亚洲激情社区| 亚洲黄页一区| 国产精品成人一区二区三区吃奶 | 久久激情五月丁香伊人| 国产精品一区二区你懂得| 久久成人精品| 久久躁狠狠躁夜夜爽| 亚洲精品一区二区在线| 一二三区精品| 国产一区在线免费观看| 亚洲高清不卡av| 国产精品久久激情| 蜜桃av一区二区三区| 欧美国产日本在线| 久久国产精品网站| 欧美电影在线| 欧美中文字幕精品| 免费在线看成人av| 欧美亚洲免费电影| 欧美www视频| 性欧美videos另类喷潮| 麻豆成人在线观看| 香蕉久久精品日日躁夜夜躁| 免费黄网站欧美| 亚洲欧美三级在线| 欧美顶级少妇做爰| 久久久久久久尹人综合网亚洲| 欧美大片在线观看一区二区| 性视频1819p久久| 欧美不卡在线视频| 久久中文字幕一区二区三区| 欧美日韩中文字幕| 欧美国产在线观看| 国产亚洲人成a一在线v站 | 亚洲综合电影| 欧美chengren| 麻豆国产精品一区二区三区 | 欧美日韩三区四区| 久热精品视频在线免费观看| 欧美性片在线观看| 亚洲国产欧美不卡在线观看| 国产主播一区二区| 亚洲欧美99| 午夜精品久久久久久久久| 欧美激情91| 欧美大片免费看| 伊人成人在线| 久久精品国产久精国产思思| 亚洲欧美激情在线视频| 欧美日韩精品一本二本三本| 美女尤物久久精品| 国产综合久久| 亚洲欧美韩国| 欧美在线观看一区| 国产伦精品一区二区三区四区免费 | 亚洲综合99| 国产精品成人观看视频免费| 亚洲精品在线观看视频| 亚洲裸体视频| 欧美日本不卡高清| 亚洲美女在线视频| 在线视频你懂得一区二区三区| 美女亚洲精品| 亚洲第一主播视频| 日韩视频在线你懂得| 欧美福利一区二区| 亚洲精选在线| 亚洲欧美日本日韩| 国产精品尤物| 欧美一区二区视频观看视频| 久久精品人人爽| 悠悠资源网久久精品| 免费成人高清| 亚洲另类自拍| 香蕉乱码成人久久天堂爱免费| 国产精品久久97| 欧美在线啊v| 免费观看欧美在线视频的网站| 在线观看av一区| 欧美日韩高清不卡| 亚洲一区bb| 久久美女性网| 亚洲精品婷婷| 国产精品一卡二| 久久久欧美精品| 亚洲三级视频| 久久精品夜色噜噜亚洲a∨| 黑人巨大精品欧美一区二区| 欧美xart系列在线观看| 中文国产成人精品| 免费观看不卡av| 亚洲色诱最新| 韩国三级电影久久久久久| 你懂的国产精品| 亚洲自拍啪啪| 亚洲电影在线播放| 午夜精品亚洲| 91久久在线视频| 国产欧美精品一区aⅴ影院| 久热精品视频在线| 一区二区三区视频在线观看| 久久亚洲图片| 亚洲一区在线观看视频 | 韩国免费一区| 欧美视频在线观看免费| 久久精品日产第一区二区三区| 亚洲日本成人女熟在线观看| 先锋影院在线亚洲| 99国产精品视频免费观看一公开 | 欧美三级电影一区| 久久激情综合| 亚洲一区二区三区在线观看视频| 麻豆av一区二区三区| 午夜亚洲一区| 一区二区久久| 亚洲国产精品久久久久婷婷老年| 国产精品久久久久免费a∨| 欧美精品色一区二区三区| 久久精品人人做人人综合| 亚洲视频在线二区| 日韩视频中午一区| 亚洲第一搞黄网站| 免费欧美在线| 久久人人爽人人| 久久国产高清| 欧美在线一二三| 亚洲欧美日韩国产中文| 一区二区三区高清在线| 亚洲精品乱码久久久久| 1024国产精品| 亚洲国产精品久久久久秋霞蜜臀| 国产夜色精品一区二区av| 国产精品久久久久久亚洲调教| 欧美日本在线播放| 欧美精品在线一区二区| 欧美成人中文字幕| 欧美高清在线精品一区| 免费日韩精品中文字幕视频在线| 老司机午夜精品| 裸体歌舞表演一区二区| 久久频这里精品99香蕉| 久久综合九色综合欧美就去吻 | 一区二区三区高清在线观看| 亚洲精品在线视频观看| 亚洲免费观看| 亚洲天堂免费观看| 午夜欧美大尺度福利影院在线看| 亚洲欧美日韩综合国产aⅴ| 欧美亚洲综合久久| 久久亚洲综合色一区二区三区| 久久久久久久国产| 欧美xxx在线观看| 欧美日本国产| 国产精品久久久久久影视 | 国产精品视频免费观看| 国产欧美日韩综合| 樱花yy私人影院亚洲| 最新精品在线| 亚洲综合色视频| 久久精彩免费视频| 欧美成人免费在线观看| 亚洲麻豆视频| 久久国产精品一区二区三区四区| 久久久噜噜噜久久中文字免| 欧美大片专区| 国产精品久久久久毛片大屁完整版| 国产免费成人av| 91久久精品美女| 亚洲欧美日韩天堂| 欧美成人免费全部观看天天性色| 亚洲精品国产精品国产自| 亚洲女人天堂成人av在线| 另类春色校园亚洲| 欧美婷婷久久| 一区二区在线视频| 国产精品99久久久久久久女警| 欧美在线视频二区| 亚洲国产精品高清久久久| 亚洲男人的天堂在线aⅴ视频| 久久嫩草精品久久久久| 国产精品国产三级国产| 亚洲国产高潮在线观看| 亚洲欧美国产日韩天堂区| 免费高清在线一区| 午夜视频在线观看一区| 欧美激情精品久久久久久免费印度| 国产精品亚洲一区| 亚洲伦理在线观看|