CArray,CList,CMap如何實化(實例化,instantiation)

 

    哎,這是乎又是一篇沒有什麼技術含量的應用性文章,但我,又能怎麼樣?雖然,我們在《CMap如何使用,用法舉例》中,已經非常詳細的說明了CMap參數的用法,也在其他的專題中介紹過了其它模板類的實化過程,不過我還是擔心是否每個人都有足夠的耐心去看完那些蜿蜒之天際的文章。所以,我們把CArray、CList以及CMap的參數問題獨立出來,在此着重的講解一番。

    還是先看一個簡單的實化例子吧:

    typdedef CMap<CString, LPCTSTR,CString, CString&>   CStrMap;

    顯然,向這樣的用法,是無可厚非的,就像我們國家的中醫,在經過無數次致命的嘗試之後,得到了這個不再苦澀的,似乎也可以包治百病的靈丹妙藥。可是,這畢竟是個令人心驚膽戰的用法,尤其對於那些並不熟悉並試圖熟悉CMap的小傢伙們。我們還是先來看看CMap類型參數列表吧:

    template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>

    它繼承了CArray以及CList的參數風格,將傳入類型與返回類型分開,其中帶有ARG_前綴的是傳入類型,另外一個則是返回類型,這已經是我們不止一次的抱怨爲什麼要將類型參數分開了,其實實踐證明,它們的確可以被合併起來,不過參數傳入的靈活性將會受到部分的影響。

    我們在《CMap如何使用,用法舉例》中,已經比較詳細的討論過爲什麼CMap的ARG_KEY與KEY可以是不同的參數類型,其實這可以推廣到所有類似的模板之中。不得不聲明的一點是:在CMap中,實際存儲的是KEY以及VALUE。那麼,現在我們來看看CStrMap到底是如何實化的。

    我們看到在CMap中,類型參數ARG_KEY的引參是LPCTSTR,而與之對應的KEY的引參卻又是CString,它們是如此的固執,既然都指代了同樣的內容,爲什麼又偏偏將對方視若不見呢?其實,這與CMap的內部實現存在着密不可分的關係,由於在CMap的內部,建立了一個Hash表,所以,必須將所有的傳入參數置換爲類似於int類型的整數,這樣纔可以符合散列函數的參數類型,否則CMap在編譯時會報錯。那麼又是爲什麼?我們可以將ARG_KEY使用LPCTSTR來實化呢?其實,這又牽扯到了Hash表的實現問題,當然其中也包含了CString的實現問題。我們知道,存儲在CStrMap內部的KEY仍然是CString,而我們對Hash表的操作,無非也就是給出一個整型的數,然後根據散列函數來尋找一個存儲地址而已,尤其,當我們使用operator[]操作符的時候,如果我們給定的KEY所對應的VALUE並不存在的話,CMap會自動的爲我們生成一個相應的元組,而如果你繼而對它賦值的話,它會將ARG_KEY直接賦值給KEY,而這裏就涉及到KEY的賦值構造函數了(其實,我更想叫它賦值操作符),現在我們使用的KEY是CString,而我們卻要將一個LPCTSTR賦值於它,所以,我們就必須重載這個函數,其實也就是這樣的一個函數:

    CString& operator=(LPCTSTR lpsz);

    顯然,這立即解決了我們的問題,不過,這僅僅是從邏輯功能層面,如果你傳入的KEY是個很長的字符串的話,那麼必然會對CStrMap的性能造成影響,其實這樣的隱患也同樣存在於VALUE身上,乃至其它類似的模板類上,這不得不引起我們的足夠重視!

C++,這門完美的近乎沒有任何瑕疵的OOP語言,無疑就是閃耀星空的曠世鉅獻,然而,這似乎並沒有引起每個人的共鳴。C++從C中的誕生,似乎就註定了,它永遠都不可能像JAVA那樣血統純正,我不得不承認,它並非一個嚴格意義上的OOP(Oriented-object language,面向對象設計語言),然而就是這種近乎不合理的缺陷,卻又造就了它近乎不合理的適應性,我是向來瞧不起解釋性語言的,因爲我本人就編寫過腳本的解釋器,實踐證明,它們的效率要遠遠的落後於編譯性語言,雖然它們常常號稱自己如何的平臺無關,如何的小而強大。

    但,對於絕大多數的程序員來說,JAVA的確有很多的優勢,也許其中之一,便是簡單易學,其實這似乎更像一個藉口。指針一度是困擾相當一部分程序員的暗物質,而C++更將這種震懾發揮到極致。顯然C++的確很難,難到令這個世界上,至少50%以上的程序員,望而卻步。然而一個幾乎包羅萬象的程序設計語言,幾乎能容忍一切的全能語言,它到底有着什麼樣的魔力,卻令另外50%的人如此的鐘意,如此的着迷?

    偏題三千里,言歸正傳。

    至於C++的性能,似乎一直被OOP的性格所掩蓋,而OOP主張的面向對象編程,更讓許多竊竊私語的程序員有了足夠的底氣發言,同時他們宣稱,C++顯然就是應該由對象統治的世界,其實--大錯特錯,簡直錯的一敗塗地。C++,歸根結底,還是由指針執政,以致我們可以直接的操縱內存,直接和系統底層會話,並且還能實現我們空前強大的令人炫目的多態。所以,我們必須要利用這個得天獨厚的資源。

    偏題一千里,再言歸正傳。

    現在,我已經知道,直接在C++程序中使用對象,是何等的不專業,顯然,我們更應該使用指針,曾經有一位高人,不無自我解嘲的說,他寫程序的第一年連一個指針都不敢用,後來又在他的嬰幼兒奶粉級的大作中大肆聲討指針,可想他曾經遭受了指針怎樣的折磨。

    在程序中使用指針,幾乎可以使你的程序恢復到C的性能級別,顯然我不會迴避你正在思考命題,不錯,C的性能的確要高於C++,而彙編語言的性能顯然要進一步的高於C,只要學過一點編譯原理的人,應該不會反對這個早已過時的辯論焦點。關鍵在於,從C過度到C++,已經是一種編程思維的轉變。然而很多人,既沒有繼承C的高性能性,又未能領悟C++的封裝、繼承與多態性,卻妄圖從C直接飛到C++上,卻被掛在了C與C++的中間,左右爲難,大概停留到了C+這門語言去了。

    言歸正傳。

    我們在定義函數的時候,應該儘可能的少傳對象,而更應該傳指針或者引用,我們知道對於編譯器內部來說,引用其實就是一個指針。這最主要的原因是,我們不必再爲函數參數的圧棧而苦惱不已;同樣,我們也不應該在函數結束之後,用對象來返回,這同樣增加了函數圧棧的開銷,而更應該適可的返回一個指針或者一個引用,當然,這樣的指針和引用,絕對不能指向一個函數體內的臨時對象。然而傳入CMap中的類型參數,到底應該是什麼,這似乎已經上升到了算法的層次,從而使我們對於實化本身的討論變的毫無意義。

    我也許會主張,將CMap實化成這種模樣:

    typedef CMap<CString*, CString*, CString*, CString*>    CStrPtrMap;

    箇中緣由,當然還是出於對效率的考慮,我們在SetAt之前先new出一個CString對象,然後將其填入CStrPtrMap,這樣的好處在於我們在SetAt的時候不需要去調用CString的賦值構造函數,而僅僅作簡單的數據拷貝,這樣的行爲幾乎適合所有的複雜數據結構,包括類,結構體。當然,唯一不能忘記的是,必須在必要的時候釋放那些new出來的CString對象,否則,將會導致嚴重的內存泄露!

    當然,你可以根據自己的興趣,去總結到底應該選擇怎麼樣的KEY與ARG_KEY。由於一般來說,KEY都不可能是太大的字符串,從而爲了兼顧效果與效率,作爲一種折中的方案就是定義這樣的一個類似於字典的CMap實化類:

    typedef CMap<CString, LPCTSTR, CString*, CString*>    CStrPtrMap;

    啊,這又是一個長着人身豬臉的八戒啊!不過,既然要去西天取經,八戒就八戒吧。

    當然,千叮萬囑的還是,別忘記釋放爲CString*申請的內存!至於另外兩個模板的參數選擇,基本上大同小異,並且要比CMap簡單,就不再羅嗦了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章