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

huaxiazhihuo

 

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

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

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

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

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

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

          但是,使用模板之后,字符串的字符數(shù)組的長(zhǎng)度只要不一樣,它們就都屬于不同類(lèi)型變量,并且之間還都不兼容呢,雖然它們都是字符串。此外,還會(huì)編譯器還將生產(chǎn)出一堆重復(fù)的代碼。這無(wú)論如何,都不能忍受。于是,自然而然,就想到了繼承。CStackString模板類(lèi)繼承于非模板的mybasestringmybasestring中實(shí)現(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;
}

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

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

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

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

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

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

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

玩具代碼 24點(diǎn)游戲

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

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

有理數(shù)類(lèi)的一點(diǎn)思考

        在寫(xiě)24點(diǎn)的程序時(shí),要處理除法運(yùn)算,不可避免地將引入小數(shù)點(diǎn),但本人對(duì)于計(jì)算機(jī)中的浮點(diǎn)數(shù)實(shí)在沒(méi)有太多的好感。權(quán)衡再三,終于決定下定決心寫(xiě)一個(gè)有理數(shù)類(lèi)(以下簡(jiǎn)稱(chēng)為CRational),以解決除法運(yùn)算這個(gè)問(wèn)題。并且有理數(shù)這個(gè)類(lèi)也是一個(gè)很好的C++練習(xí)題,可以用來(lái)練練手,以下將看到,它的實(shí)現(xiàn)雖然不難,但也不是很容易。有理數(shù)這個(gè)東西,屬于用戶(hù)自定義的數(shù)據(jù)類(lèi)型,c++對(duì)此的支持,真可謂完美,既不失效率,又具備美觀。c++可以讓程序員做出來(lái)的自定義類(lèi)型,其行為可以表現(xiàn)得好像是由語(yǔ)言層面實(shí)現(xiàn)的那個(gè)樣子,不管從語(yǔ)法、安全、效率上講。這一點(diǎn),所有的語(yǔ)言都沒(méi)法和c++媲美。
        不管怎么說(shuō),CRational的需求相當(dāng)明確,它一定有分子、分母、然后支持加減乘除這四種運(yùn)算,于是,一口氣馬上就能寫(xiě)下它的定義。
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ù)名中,沒(méi)有加Get的前綴,實(shí)在是因?yàn)榇a中它出現(xiàn)的地方太多了,所以能避免就盡量避免。沒(méi)有涉及資源分配,析構(gòu)函數(shù)可以忽略,拷貝構(gòu)造函數(shù)和賦值函數(shù)也不用寫(xiě)了,編譯器將會(huì)提供缺省的實(shí)現(xiàn),足以滿(mǎn)足我們的要求。貌似應(yīng)該還要有一個(gè)返回求取小數(shù)值的操作,但是,這個(gè)類(lèi)本身就是為了避免操作小數(shù)點(diǎn),而且,計(jì)算小數(shù)點(diǎn)值可通過(guò)分子/分母的方法計(jì)算出來(lái)。總之,CRational的終極接口就是這個(gè)樣子了。
        每個(gè)有理數(shù)都有一個(gè)標(biāo)準(zhǔn)的等價(jià)類(lèi),這個(gè)標(biāo)準(zhǔn)的有理數(shù)的分子、分母都不能再約分了,而且可以暫時(shí)假設(shè)符號(hào)位出現(xiàn)于分子中,分母則為正整數(shù),比如說(shuō),3/6、-4/-8都等價(jià)于1/2,因此,在這個(gè)有理數(shù)類(lèi)中,必須有一個(gè)標(biāo)準(zhǔn)化的操作,問(wèn)題是,這個(gè)標(biāo)準(zhǔn)化函數(shù)是返回一個(gè)標(biāo)準(zhǔn)的有理數(shù)還是將有理數(shù)自身直接就標(biāo)準(zhǔn)化了。經(jīng)過(guò)多方面的權(quán)衡,特別是為了兼容現(xiàn)有的整型變量(int, char, short等),整型變量可看成分母為1的有理數(shù),我決定讓CRational一直處于標(biāo)準(zhǔn)化的狀態(tài)下,標(biāo)準(zhǔn)化的狀態(tài)由CRational自己來(lái)維持,客戶(hù)無(wú)須知道標(biāo)準(zhǔn)化的這個(gè)細(xì)節(jié),因此,將void standarlize()聲明于其private的區(qū)域下,其實(shí)現(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;
}

        其中,毫無(wú)疑問(wèn),gcd為最大公約數(shù),gcd沒(méi)有訪問(wèn)CRational的任何非靜態(tài)(nonstatic)變量,因此,它必將不可成為CRational的非靜態(tài)函數(shù)。將gcd聲明為靜態(tài)函數(shù),雖然可避免污染全局空間,但也使得外部代碼必須通過(guò)CRational來(lái)調(diào)用gcd函數(shù),語(yǔ)法上不方便,而且,gcd本身就是一個(gè)獨(dú)立性很強(qiáng)而且又通用的函數(shù),從意義上,它也不應(yīng)該屬于CRational里面的東西。因此,gcd只能為全局函數(shù)。關(guān)于靜態(tài)函數(shù)和全局函數(shù)的選擇,鑒于“類(lèi)的接口要盡可能的小”的原則和其他的一些問(wèn)題,只要函數(shù)不訪問(wèn)類(lèi)的靜態(tài)變量,也不通過(guò)類(lèi)的變量來(lái)訪問(wèn)到其非靜態(tài)成員,它就應(yīng)該是全局函數(shù)。全局函數(shù)是好東西,某些語(yǔ)言為了堅(jiān)持所謂的純粹的面向?qū)ο螅室獠恢С郑瑢?shí)在讓人用起來(lái)很不痛快,它的污染全局空間的問(wèn)題,完全可以通過(guò)命名空間來(lái)解決。總之,只要可能的話(huà),就應(yīng)該將函數(shù)聲明為全局函數(shù),沒(méi)什么不好。好了,請(qǐng)看gcd的實(shí)現(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;
}
 
        其算法源于《編程之美》,可看成是非遞歸的版本。咦,怎么會(huì)這么長(zhǎng),與日常所見(jiàn)的到似乎不太一樣,高效算法的代碼貌似都會(huì)很長(zhǎng),好比strlen。再仔細(xì)看,里面居然沒(méi)有取余的操作。嗯,它的算法核心,用移位和減法這兩種快速的運(yùn)算來(lái)替代取模這種慢速運(yùn)算。
有了standarlize()之后,CRational的幾個(gè)函數(shù)的實(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的情況,然后拋出異常。但是,這屬于契約使用的問(wèn)題,用戶(hù)違背的契約,一切后果,必須自己承擔(dān),我們的代碼無(wú)須對(duì)此負(fù)責(zé)。
        此外,就是各種+、-、*、/、==、輸出等各種全局運(yùn)算符重載的操作了。得益于C++的缺省類(lèi)型轉(zhuǎn)換,我們不用再做其他事情,就可以很好地讓我們的CRational很好地與融入到原有的各種整型世界中去。當(dāng)然,為了效率起見(jiàn),似乎有必要針對(duì)各種整型提供+、-、*、/的各種重載版本(complex就是這樣做的),但在此,確實(shí)沒(méi)有必要。缺省類(lèi)型轉(zhuǎn)換有時(shí)雖然會(huì)帶來(lái)一些問(wèn)題,但是,當(dāng)確實(shí)需要它的時(shí)候,它就能發(fā)揮重大作用了。C++的各種特性就是這樣,你可以不用,它也不打擾你(你要故意或無(wú)意用錯(cuò),那也沒(méi)辦法),當(dāng)真正需要到的時(shí)候,特性的威力就顯示出來(lái)了。很多人之所以愿意沉迷于C++,就在于它不剝奪程序員的任何一點(diǎn)選擇的權(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;
}
……
        嗯,一再寫(xiě)這些入門(mén)級(jí)的文章,本座也很自覺(jué)有失身價(jià),也算是對(duì)網(wǎng)絡(luò)世界的一點(diǎn)回報(bào)吧。只要有一個(gè)人看了,能有所啟發(fā),在下就心滿(mǎn)意足了,為免誤人子弟,也歡迎有人批評(píng)指正。

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

神奇的C數(shù)組

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

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

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

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

類(lèi)設(shè)計(jì)一則,GDI對(duì)象選入器

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

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

 

 

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

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

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

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

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

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

WINDOWS與設(shè)計(jì)模式

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

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

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

試論C++類(lèi)庫(kù)開(kāi)發(fā)之難

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

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

MFC,一開(kāi)始就錯(cuò)了

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

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

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

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

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

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

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

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

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

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

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

        突然驚覺(jué),C++的類(lèi)庫(kù)框架,無(wú)不充滿(mǎn)了各種各樣的缺陷,除了STL還差強(qiáng)人意之外(其實(shí)也問(wèn)題多多),沒(méi)有一個(gè)能夠讓人看著滿(mǎn)意,用著放心。于是,我要投入LISP和C的懷抱之中了,不,一定要頂住,C++是我的最?lèi)?ài)。

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

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

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

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

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

導(dǎo)航

統(tǒng)計(jì)

常用鏈接

留言簿(6)

隨筆分類(lèi)

隨筆檔案

搜索

積分與排名

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产亚洲电影| 国产亚洲一本大道中文在线| 亚洲激情在线视频| 欧美高清你懂得| 欧美屁股在线| 午夜亚洲视频| 久久久精品性| 亚洲精品欧美精品| 亚洲一区二区三区三| 国产一区二区三区四区三区四| 久久天天躁狠狠躁夜夜av| 久久综合成人精品亚洲另类欧美| 亚洲高清不卡在线| 中文精品视频一区二区在线观看| 国产精品自在线| 欧美国产先锋| 国产精品一区二区三区久久| 欧美mv日韩mv国产网站app| 欧美jizz19hd性欧美| 午夜精品在线看| 久久综合伊人77777| 亚洲一区免费网站| 久久亚洲私人国产精品va| 亚洲四色影视在线观看| 久久精品国产精品亚洲| 亚洲天堂av图片| 久久久中精品2020中文| 亚洲综合色视频| 免费观看欧美在线视频的网站| 亚洲制服欧美中文字幕中文字幕| 久久久久久久久久久久久9999| 一区二区三区**美女毛片| 久久精品久久99精品久久| 亚洲香蕉网站| 欧美国产一区二区在线观看| 久久免费高清视频| 国产精品福利久久久| 亚洲国产精品一区| 国产亚洲综合在线| 国产精品99久久久久久人| 亚洲人成人77777线观看| 欧美中在线观看| 欧美亚洲一区在线| 国产精品海角社区在线观看| 欧美激情一区在线观看| 精品999日本| 亚欧成人精品| 久久成人一区| 国产日产亚洲精品| 亚洲线精品一区二区三区八戒| 亚洲国产日韩一区| 久久久在线视频| 另类av导航| 亚洲大胆av| 另类激情亚洲| 亚洲国产导航| 久久久久久999| 免费毛片一区二区三区久久久| 国产日韩精品一区| 性欧美videos另类喷潮| 欧美一区二区视频在线观看| 国产精品免费观看在线| 中文亚洲欧美| 性欧美18~19sex高清播放| 欧美视频二区| 亚洲一区二区三区在线看| 午夜精品福利一区二区蜜股av| 国产精品久久久久久久电影| 亚洲最新在线视频| 亚洲一区二区成人| 国产精品久久久久三级| 亚洲欧美久久| 久久综合九色综合久99| 亚洲国产日韩美| 欧美伦理影院| 一区二区三区欧美| 香蕉精品999视频一区二区| 国产精品亚洲аv天堂网| 午夜精品久久久久久久男人的天堂| 欧美一区二区三区电影在线观看| 国产欧美一区二区三区在线看蜜臀 | 欧美国产日韩a欧美在线观看| 欧美国产日韩在线观看| 一区二区三区国产盗摄| 六月婷婷一区| 亚洲福利视频在线| 欧美日韩免费观看一区二区三区| 亚洲深夜激情| 久久综合久久综合久久| 夜夜嗨av色综合久久久综合网| 国产精品久久一区主播| 久久精品国产第一区二区三区| 91久久极品少妇xxxxⅹ软件| 亚洲欧美日韩中文在线制服| 黄色日韩精品| 欧美日韩综合不卡| 久久久精品性| 亚洲视频一区| 美女免费视频一区| 亚洲午夜高清视频| 精品va天堂亚洲国产| 欧美色视频在线| 久久亚洲综合色| 亚洲永久免费观看| 亚洲国产精品一区在线观看不卡| 亚洲欧美另类中文字幕| 在线激情影院一区| 国产精品亚洲片夜色在线| 久久综合久久综合久久综合| 亚洲午夜精品久久久久久浪潮| 欧美不卡视频一区发布| 欧美一区二区精品在线| 亚洲美女中出| 亚洲国产精品成人va在线观看| 国产精品av久久久久久麻豆网| 免费成人av在线| 久久成人免费视频| 午夜激情综合网| 在线视频免费在线观看一区二区| 欧美激情视频一区二区三区免费 | 欧美激情1区2区| 久久国产精品一区二区| 亚洲神马久久| 99re6热在线精品视频播放速度| 国产主播一区二区| 国产精品视频你懂的| 欧美日韩国产区一| 欧美成人三级在线| 久久综合九色综合欧美就去吻| 亚洲欧美日韩一区二区在线 | 久久福利精品| 亚洲欧美日韩国产中文在线| 亚洲欧洲日产国产网站| 亚洲第一福利在线观看| 欧美 日韩 国产一区二区在线视频 | 国产精品香蕉在线观看| 欧美日韩在线看| 欧美色精品在线视频| 欧美日韩午夜| 欧美午夜免费| 国产精品免费网站在线观看| 欧美亚洲第一区| 国产精品网站视频| 国产精品视频一| 国产欧美精品在线播放| 国产精品综合| 韩国欧美国产1区| 亚洲国产日韩精品| 日韩视频永久免费| 中国成人在线视频| 午夜精品福利在线观看| 久久国产主播精品| 欧美成人国产一区二区| 亚洲第一区在线观看| 亚洲精品日产精品乱码不卡| 在线综合欧美| 欧美在线视频导航| 蜜乳av另类精品一区二区| 欧美另类高清视频在线| 国产精品老牛| 激情视频一区| 亚洲免费成人av| 性8sex亚洲区入口| 欧美成人精品三级在线观看| 91久久午夜| 亚洲一区在线视频| 老司机精品福利视频| 欧美日韩在线综合| 国产在线成人| 日韩香蕉视频| 久久久蜜桃精品| 亚洲精品一级| 久久精品导航| 欧美私人啪啪vps| 在线精品国精品国产尤物884a| 亚洲精品老司机| 久久精品一本| 亚洲精品日韩欧美| 久久裸体艺术| 国产精品成人在线观看| 18成人免费观看视频| 亚洲免费一级电影| 亚洲电影在线看| 欧美一区二区三区久久精品 | 亚洲欧美视频一区| 欧美精品在线观看91| 国内精品久久久| 亚洲天堂成人在线观看| 欧美高清视频一区二区| 午夜精品999| 欧美日韩福利| 亚洲精品三级| 毛片一区二区三区| 亚洲欧美一区二区三区久久| 欧美激情网站在线观看| 在线日韩欧美视频| 久久高清国产| 亚洲午夜精品久久久久久app| 欧美国产成人在线| 亚洲国产清纯|