• <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>

            huaxiazhihuo

             

            消息發送VS設計模式 C++沉思錄的一練習題的另解

                 摘要:         緣起,看到一遍文章,題材取自于《C++沉思錄》,綜合利用好幾種設計模式,并且結合所謂的面向對象的技巧,然后洋洋自得,并且在最后,還反問:“有更好的解決方案嗎?”。本座暗嘆,又一個設計模式的毒害者。以下,就提出另一種解決方案。      &...  閱讀全文

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

            基于堆棧上的字符串實現

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

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

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


            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就又聯系在一塊了。Mybasestring可看成定義了數據接口的基類,其子類的頭部數據必須與它保持一致,嗯,很好。然后,在mybasestring中實現的各種功能,都可以用在mystring身上了,而CStackString中無須實現任何功能,它只負責在堆棧上分配內存。所以,mybasestring不僅可以用在堆棧上,還能用于堆上,只要再繼續定義一個能在堆上分配內存的mybasestring的子類即可,然后都能相容于堆棧上的CStackString字符串。

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

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

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

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

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

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

            玩具代碼 24點游戲

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

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

            有理數類的一點思考

                    在寫24點的程序時,要處理除法運算,不可避免地將引入小數點,但本人對于計算機中的浮點數實在沒有太多的好感。權衡再三,終于決定下定決心寫一個有理數類(以下簡稱為CRational),以解決除法運算這個問題。并且有理數這個類也是一個很好的C++練習題,可以用來練練手,以下將看到,它的實現雖然不難,但也不是很容易。有理數這個東西,屬于用戶自定義的數據類型,c++對此的支持,真可謂完美,既不失效率,又具備美觀。c++可以讓程序員做出來的自定義類型,其行為可以表現得好像是由語言層面實現的那個樣子,不管從語法、安全、效率上講。這一點,所有的語言都沒法和c++媲美。
                    不管怎么說,CRational的需求相當明確,它一定有分子、分母、然后支持加減乘除這四種運算,于是,一口氣馬上就能寫下它的定義。
            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;
            }
            ;
                     嗯,我承認代碼很匈牙利,中MFC的毒太深。毋庸置疑,分子分母只能獲取,不能設置,函數名中,沒有加Get的前綴,實在是因為代碼中它出現的地方太多了,所以能避免就盡量避免。沒有涉及資源分配,析構函數可以忽略,拷貝構造函數和賦值函數也不用寫了,編譯器將會提供缺省的實現,足以滿足我們的要求。貌似應該還要有一個返回求取小數值的操作,但是,這個類本身就是為了避免操作小數點,而且,計算小數點值可通過分子/分母的方法計算出來。總之,CRational的終極接口就是這個樣子了。
                    每個有理數都有一個標準的等價類,這個標準的有理數的分子、分母都不能再約分了,而且可以暫時假設符號位出現于分子中,分母則為正整數,比如說,3/6、-4/-8都等價于1/2,因此,在這個有理數類中,必須有一個標準化的操作,問題是,這個標準化函數是返回一個標準的有理數還是將有理數自身直接就標準化了。經過多方面的權衡,特別是為了兼容現有的整型變量(int, char, short等),整型變量可看成分母為1的有理數,我決定讓CRational一直處于標準化的狀態下,標準化的狀態由CRational自己來維持,客戶無須知道標準化的這個細節,因此,將void standarlize()聲明于其private的區域下,其實現如下:
            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為最大公約數,gcd沒有訪問CRational的任何非靜態(nonstatic)變量,因此,它必將不可成為CRational的非靜態函數。將gcd聲明為靜態函數,雖然可避免污染全局空間,但也使得外部代碼必須通過CRational來調用gcd函數,語法上不方便,而且,gcd本身就是一個獨立性很強而且又通用的函數,從意義上,它也不應該屬于CRational里面的東西。因此,gcd只能為全局函數。關于靜態函數和全局函數的選擇,鑒于“類的接口要盡可能的小”的原則和其他的一些問題,只要函數不訪問類的靜態變量,也不通過類的變量來訪問到其非靜態成員,它就應該是全局函數。全局函數是好東西,某些語言為了堅持所謂的純粹的面向對象,故意不支持,實在讓人用起來很不痛快,它的污染全局空間的問題,完全可以通過命名空間來解決。總之,只要可能的話,就應該將函數聲明為全局函數,沒什么不好。好了,請看gcd的實現。
            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。再仔細看,里面居然沒有取余的操作。嗯,它的算法核心,用移位和減法這兩種快速的運算來替代取模這種慢速運算。
            有了standarlize()之后,CRational的幾個函數的實現如下所示:
            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;
            }
            ……
                    構造函數中,似乎應該檢查分母為0的情況,然后拋出異常。但是,這屬于契約使用的問題,用戶違背的契約,一切后果,必須自己承擔,我們的代碼無須對此負責。
                    此外,就是各種+、-、*、/、==、輸出等各種全局運算符重載的操作了。得益于C++的缺省類型轉換,我們不用再做其他事情,就可以很好地讓我們的CRational很好地與融入到原有的各種整型世界中去。當然,為了效率起見,似乎有必要針對各種整型提供+、-、*、/的各種重載版本(complex就是這樣做的),但在此,確實沒有必要。缺省類型轉換有時雖然會帶來一些問題,但是,當確實需要它的時候,它就能發揮重大作用了。C++的各種特性就是這樣,你可以不用,它也不打擾你(你要故意或無意用錯,那也沒辦法),當真正需要到的時候,特性的威力就顯示出來了。很多人之所以愿意沉迷于C++,就在于它不剝奪程序員的任何一點選擇的權利。
            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;
            }
            ……
                    嗯,一再寫這些入門級的文章,本座也很自覺有失身價,也算是對網絡世界的一點回報吧。只要有一個人看了,能有所啟發,在下就心滿意足了,為免誤人子弟,也歡迎有人批評指正。

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

            神奇的C數組

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

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

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

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

            類設計一則,GDI對象選入器

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

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

             

             

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

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

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

            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;
            }
            ;

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

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

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

            WINDOWS與設計模式

                    作為C++的堅實粉絲,我一直很排斥JAVA,并不是JAVA這種語言不好,而是Java迷的那副嘴臉,事事都要與C++爭,并且還堅稱JAVA比C++,甚至連執行效率都要勝過C++,什么JIT運行時能監視代碼,選取執行頻率最高的代碼,根據特定的平臺,進行特別處理,生產出最優化的機器代碼;又說什么垃圾回收能夠解決C++中的內存管理問題,并且內存分配遠遠勝過C++中的手工人肉管理內存的毛病,其實,內存管理從來就不是C++中的嚴重問題,只要設計得好,應用層中的代碼甚少可以不出現new, delete。至少delete可以不出現,大家知道WHY的;種種論調,無理取鬧之極。而最令我受不了的,就是他們閉口開口,都離不開設計模式,搞得設計模式好像成為了JAVA的專有產品。須知,第一本設計模式的書,也是最最經典的那本四人書,其對設計模式的實現語言,采用的就是SmallTalk 和C++,一開始就不關JAVA的一點事情,并且書中C++還占了較大的比重。關于四人書,最讓我欣慰的一件事情就是,四位巨頭并沒有響應JAVA迷的強烈呼聲,采用JAVA來實現設計模式的第二版。當然,我也承認,用JAVA來實現設計模式,確實來得要比C++清爽,JAVA的這種語言,好像就是專為設計模式量身訂做。只可惜,市面上任何一本JAVA的設計模式書,沒有一本及得上我們C++的那一本設計模式圣經,C++中不必再需要設計模式的書了,因為最好的書就已經擺在那里了,涵蓋了設計模式中的方方面面,濃縮精華得很。突然想起,C++的教材也不需要那么多,因為老爺子已經寫了一本最好的書了,其他書的內容,都已經涵蓋在那本C++語言圣經中了。至于那些不被C++圣經所提及的,那都是一些走火入魔的玩意,玩玩還可以,開闊開闊視野也不錯,但真要用在實際項目中,還是少用為妙。罪過罪過,C++中的好書確實有好幾本,不宜一棍子打死。
                    有趣的是,設計模式在JAVA中被捧上了天,沒有設計模式,JAVA就沒法活下去。反而C++作為設計模式的第一實現語言,卻不怎么感冒。不能說被輕視,但起碼沒有普遍被重視。即使C++中的高手,也沒有對設計模式如何如何的推崇備至,他們貌似更加喜歡玩語法糖,熱衷于在C++中模擬出其他語言的特性。這也難怪,C++有四種編程典范,設計模式的主要應用就在于面向對象,面向對象不過是其中最不成熟的一種,在C++中,最成熟的要數基于對象的編程,當然,拜C語言成熟,面向過程也熟得要爛了,泛型這東西也挺前衛的,而C++中的面向對象,一直臭名昭著,無論怎么努力,都難以搬上臺面。注意,C++中的面向對象與虛函數是兩碼事,至少在我看來是這樣的。
                    好了,該談談我對設計模式的看法了,一句話,我對設計模式殊無好感,源于我內心對當前的面向對象編程風格的排斥,因為那是偽面向對象編程,所謂的偽面向對象,就是指通過單根繼承和針對接口或抽象類來寫代碼,就以為是在進行面向對象的編程,那實在是太天真了。設計模式中的那些點子,用得好,確實能解決偽面向對象的一些問題,無可厚非;用得不好,無緣無故地引入一些不必要的東西,模式的應用意味著間接性的增厚。現實中,能恰當地用好模式的人,少之又少,模式總是出現在一些不必要出現的場合下。很多人都是生吞活剝了23種模式之下,內心就沾沾自喜,到處躍躍欲試,鮮有人嘗試理解模式背后的統一思想是什么,或者說,如果代碼本身就已經能夠很好類與類之間的耦合問題,可勝過千萬種設計模式。
                    以下文字,與孟巖大俠的觀點,存在部分重復之處,讀者認為在下是在拾他的牙慧,我也不反對,畢竟人家的文章反表在前,我無話可說,本文的重點,旨在表達在下對設計模式的鄙視。
                    既然有偽面向對象,就有真面向對象。真面向對象的編程,就是你只知道一個對象的ID,其他的一切事情,它繼承自那些父類,實現了那些接口,統統一概都不得而知,然后你只能通過這個ID給對象發送消息,以此來驅使對象做一些事情,注意,確確實實是只是發送消息,而不是調用該對象的函數,那怕是調用了該對象實現的接口函數,也意味著該對象的依賴,好吧,說錯了,是對該接口的依賴,不管怎么樣,都是依賴,而且依賴接口,還搞得客戶代碼和對象都要依賴于同一個接口了。
            給對象發送消息,聽起來似乎有點抽象,但是,只要聯想到WINDOWS的窗口句柄和消息處理函數,就自然明白是怎么回事了。在WINDOWS下,每一個窗口都是一個對象,窗口句柄代表了對象ID。操作窗口時,只能通過SendMessage或PostMessage來讓窗口作一些事情。SendMessage或PostMessage的四個參數,分別是對象ID、消息編號、消息參數1和消息參數2。客戶代碼在使用窗口時,不需要假設什么接口,能做的,僅須做的,僅僅就是給它發送消息,客戶代碼完全無須依賴于什么鬼接口,非常簡單明了。但是,這里存在一個問題,消息參數1和消息參數2都是void*類型,不安全耶,要是誤發送錯了消息,那該怎么辦。在偽面向對象中,客戶也可能在調用接口參數時,也可能會傳錯了參數,只不過是編譯器可以幫你找出來。其實類型不安全,也沒什么,客戶既然要發送消息給對象,自然要遵守使用的協議,自然要清楚自己在做什么事情,這本來就是C語言的精神,一切相信程序員。
                    好了,該是給WINDOWS大唱贊歌了。WINDOWS系統的窗口是我見過中算是比較象樣的面向對象的典范了。將面向對象的精神貫徹到底,窗口對象很好地做到了僅僅是純粹解析消息,執行消息,與外界的其他對象,不存在任何一點耦合。一個窗口對象中的lpfnWndProc其實可以理解成指向虛函數表的指針,但是它卻要比虛函數表的指針功能更加強大靈活,并且還不存在虛擬函數表的種種問題。強大之處,就之于lpfnWndProc相當于指向一個龐大的虛函數表,這個表有2的32次方多個虛函數,靈活之處可以突破虛函數的種種限制。當你要給一個窗口對象添加新的功能,或者不滿意它的某些原有操作的時候,你完全可以子類化該窗口,在子類化操作中,截取關心的消息,解析消息,執行消息,至于其他的消息,直接交給lpfnOldWndProc或DefWindowProc就是了。從這個意義上講,所有的窗口對象繼承于DefWindowProc,而子類化窗口即是繼承于lpfnOldWndProc,但是,這里的繼承,卻沒有偽面向對象中的繼承或實現接口的種種弊端。而且,在你子類化窗口的時候,不用影響到客戶的任何一點點代碼,客戶代碼依舊是發送消息,卻不知道窗口對象已經舊貌換新顏了,OH,這實在太美妙了。所有的耦合,奇跡般的消失得干干凈凈了。消息比之于接口,那完完全全是純粹的正交關系,并且沒有接口的種種累贅,種種缺陷。并且更爽的是,即使對象沒有處理消息,也沒有關系,客戶代碼依然可以給對象發送消息。
                    從某種意義上講,設計模式不過是為了解耦對象與對象之間的耦合關系,當對象之間不存在耦合的時候,設計模式還有什么存在的意義嗎?以下結合WINDOWS系統來理解,所謂的設計模式,不過是消息發送的一些應用罷了。下面的舉例,例子之間沒有什么必然關系,我想到那里就寫到什么,模式后面似乎應該帶上英文,但我也懶得寫了。

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

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

            試論C++類庫開發之難

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

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

            MFC,一開始就錯了

                    謹以此文祭奠過去多少年下來,日日夜夜學習MFC源代碼的寶貴時光。

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

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

                    好吧,廢話少說,趕緊進入正題。

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

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

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

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

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

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

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

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

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

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

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

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

            僅列出標題
            共5頁: 1 2 3 4 5 

            導航

            統計

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            精品久久久久久无码中文野结衣 | 久久久免费观成人影院| 99久久99久久精品国产片果冻| 国产精品久久久久…| 高清免费久久午夜精品| 久久久网中文字幕| 久久久久免费看成人影片| 亚洲成色999久久网站| 亚洲精品无码久久毛片 | 久久久久四虎国产精品| 狠狠色丁香婷婷综合久久来来去 | 久久男人中文字幕资源站| 久久精品国产免费观看| av无码久久久久久不卡网站| 久久久久久av无码免费看大片| 久久国产免费直播| 久久精品成人欧美大片| 亚洲国产一成人久久精品| 久久男人AV资源网站| 国产巨作麻豆欧美亚洲综合久久| 亚洲国产精品无码久久一区二区 | 久久这里有精品视频| 久久久久亚洲av无码专区| 18禁黄久久久AAA片| 国产精品成人无码久久久久久| 久久久久亚洲AV无码专区首JN | 伊人情人综合成人久久网小说| 精品午夜久久福利大片| 日韩AV无码久久一区二区| 久久无码AV一区二区三区| 欧美精品一区二区久久| 久久99精品久久久久久噜噜| 国产精品视频久久久| 国产精品美女久久久m| 久久久久亚洲精品无码蜜桃 | 久久无码人妻一区二区三区| 精品久久久久久国产| 亚洲精品乱码久久久久久自慰| 国产A三级久久精品| 免费久久人人爽人人爽av| 久久婷婷是五月综合色狠狠|