• <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>
            posts - 16,  comments - 34,  trackbacks - 0
            共10頁(yè): First 2 3 4 5 6 7 8 9 10 
            re: Dictionary的囧狀 OwnWaterloo 2009-10-15 23:14
            C#的template能做什么我不太清楚。

            C++支持編譯時(shí)的ducking type機(jī)制。
            拋棄這種強(qiáng)大的抽象機(jī)制不用, 轉(zhuǎn)而在C++這種暫不提供GC的語(yǔ)言中使用接口來(lái)作為數(shù)據(jù)結(jié)構(gòu)之間的紐帶 ……
            所以…… 我說(shuō)這不是C++的style ……

            還有一些小地方。 比如main 的返回類型是int, 而不是C#中的void。
            以單下劃線接大寫字母開(kāi)頭,以及以雙下劃線開(kāi)頭的標(biāo)識(shí)符在C++中是被保留的。
            最好不要將C#中的那些習(xí)慣帶到C++中來(lái)……
            用Type, Type_, 別用_Type。

            這些被保留的標(biāo)識(shí)符不會(huì)被永遠(yuǎn)保留。 _Bool, _LongLong, _Complex已經(jīng)出現(xiàn)。



            http://www.shnenglu.com/Streamlet/
            這位同學(xué), 和你在做類似的事情, 也遇到了類似的問(wèn)題。
            你可以參考參考……
            re: Dictionary的囧狀 OwnWaterloo 2009-10-15 22:52
            我說(shuō)具體一點(diǎn)吧……
            如果用GP(代碼一切從簡(jiǎn)):
             
            template<typename T>
            class /*array*/list {
                T *first,*last_;
            public:
                typedef T* iterator;
                typedef const T* const_iterator;
             
                iterator begin() { return first_; }
                iterator end() { return last_; }
                const_iterator begin() const { return first_; }
                const_iterator end() const { return last_; } 
                // ...
            };
             
            要迭代一個(gè)容器:
            list<int> l;
            for ( list<int>::iterator first = l.begin(), last = l.end(), first!=last; ++first)
                visit_element( *first );
             
            而你遇到的問(wèn)題就是:
            list<pair<key,value> > d;
            如何得到一個(gè)迭代器, 僅僅訪問(wèn)key部分, 而不訪問(wèn)value部分。
             
            template<class It>
            class project_first {
                It it_;
            public:
                project_first( It it ) : it_(it) {}
                typename std::iterator_traits<It>::value_type::first_type&
                    operator*() const { return it->first; }
                // 再實(shí)現(xiàn) ++, -- +n 等操作...
            };
             
            for ( project_first first = d.begin(), last = d.end(); first!=last; ++first)
                visit_key( *first );
             
             
            如果d是 list<tuple<key,value> > 類型, project_iterator還可以做得更范化一些。
             
            沒(méi)有虛函數(shù)調(diào)用,沒(méi)有動(dòng)態(tài)內(nèi)存分配。
            并且,和stl的算法,boost的算法,以及其他C++組件合作良好。
            re: Dictionary的囧狀 OwnWaterloo 2009-10-15 22:30
            @Lyt
            你也發(fā)現(xiàn)問(wèn)題了不是? 內(nèi)存不好管理。

            如果你用GP的思想去實(shí)現(xiàn)Dictionary, GetKeys可以很簡(jiǎn)單,并且很高效的實(shí)現(xiàn)。
            用OOP? 就等著承受虛函數(shù)開(kāi)銷以及內(nèi)存管理的困擾吧。。。
            然后,你又會(huì)為此去寫智能指針, 寫內(nèi)存池, 寫鎖 ……

            你可以把這些當(dāng)作練習(xí)…… 但這不是C++的style ……
            re: Dictionary的囧狀 OwnWaterloo 2009-10-15 13:43
            1.
            error C2682: cannot use 'dynamic_cast' to convert from 'const Lyt::Collection::List<_Type> *' to 'Lyt::Collection::IList<_Type>
             
            明白了嗎?
             
            2.
            IDictionary.h  include  List.h
            Dictionary.h include IDictionary.h
            相互包含在哪?
             
             
             
            C++不是這么用的……  被C#熏昏頭的同學(xué)……
            玩模板,就不要用vc6 ……

            拷貝構(gòu)造函數(shù)的正確寫法是:
            Test(const Test<T>& );
            @陳梓瀚(vczh)
            需求是iterator類型推導(dǎo)iterator解引用后的類型?
            嘿嘿,真的不能嗎?

            #include <iterator>
            MyIt it1,it2;
            typename std::iterator_traits<MyIt>::value_type // 這就是你要的
            v = *it1;

            // 除此之外還有其他幾種類型信息。
            typename std::iterator_traits<MyIt>::pointer p = &v;
            typename std::iterator_traits<MyIt>::reference r = v;
            typename std::iterator_traits<MyIt>::difference_type d
            = std::distance(it1,it2);

            typename std::iterator_traits<MyIt>::iterator_category tag;
            tag決定了iterator能做什么。

            algorithm中的所有需要iterator的算法,都和容器完全無(wú)關(guān)。
            和iterator相關(guān)的類型信息都是這么取得的。
            自然不存在M*N(M=算法的數(shù)量,N=容器的數(shù)量)的說(shuō)法。
            而是M(M=算法數(shù)量)。


            給你舉個(gè)例子可能比較明顯:
            需要value_type的算法我一時(shí)還想不出來(lái)……
            來(lái)個(gè)其他的需要difference_type的意思意思:
            template<typename InIt,typename T>
            typename iterator_traits<InIt>:: difference_type // 返回類型
            count(InIt first,InIt last,const T& v) {
            typename iterator_traits<InIt>:: difference_type c = 0;
            for (;first!=last; ++first) if (*first==v) ++c;
            return c;
            }



            這是GP的類型推導(dǎo)方式 —— 嵌入類型。這用法漂亮嗎?


            實(shí)現(xiàn)這一魔法的工作:
            iterator_traits只需實(shí)現(xiàn)一次。
            各個(gè)容器其實(shí)不需要做什么工作。
            工作在每一個(gè)iterator類型上,它必須有這幾種嵌套類型。
            當(dāng)然,iterator頭文件中提供了std::iterator, 可以繼承自它而得到這幾種嵌套類型。
            不過(guò)我一般喜歡自己寫,繼承可能會(huì)影響空基類優(yōu)化。
            多嗎?


            而且,有一點(diǎn)OOP是辦不到的。 所有的iterator都不需要有一個(gè)"基類"。
            T arr[size];
            &arr[0] 類型是T*, 是iterator,而且還是random_access。但是它的基類應(yīng)該是什么才好呢?
            它只有4字節(jié)。OOP就那個(gè)vptr就4字節(jié)了,加點(diǎn)數(shù)據(jù)再加上padding,8字節(jié)至少。



            "反而vector<int>也想要支持的話,你會(huì)發(fā)現(xiàn)所有的容器都要寫一遍……"
            這是什么需求? 需要所有容器都寫一遍? 說(shuō)出來(lái)看看?
            re: 二分查找學(xué)習(xí)札記 OwnWaterloo 2009-10-10 14:16
            @唐僧

            mix沒(méi)有右移?
            不過(guò)嘛…… 右移是除法的子集…… 沒(méi)有也挺合理的…

            嗯,在你介紹之前, 我完全不知道有uniform這回事……
            高爺爺?shù)臅袝r(shí)間一定得去看看……

            sgi的stl中有非公開(kāi)的紅黑樹(shù)實(shí)現(xiàn)。4中關(guān)聯(lián)容器其實(shí)是紅黑樹(shù)的adapter...
            就像std::stack,queue,priority_queue一樣…… 尷尬……

            我覺(jué)得吧,二分只要寫正確就行了…… 復(fù)雜度肯定是lgN的。
            lgN已經(jīng)很猛了…… 常數(shù)稍微差點(diǎn)也壞不到哪去…… 優(yōu)化是無(wú)底洞……
            不記得在哪本書上看到過(guò)這么一句話"只需要讓軟件足夠快"
            re: 二分查找學(xué)習(xí)札記 OwnWaterloo 2009-10-09 15:27
            @唐僧

            對(duì)對(duì),我忘了說(shuō)了,如果查找目標(biāo)不在區(qū)間中,2-way肯定比3-way高效。


            -------- 關(guān)于 uniform --------
            "因?yàn)椤眲?dòng)態(tài)“尋找二分點(diǎn)要比預(yù)先生成靜態(tài)二分點(diǎn)多增加很多運(yùn)算,而且這種增加是隨著搜索空間的增大而增長(zhǎng)的(這句話我不太確定,直覺(jué)上一倍左右的差距)。"

            上次我就說(shuō)了嘛, 關(guān)于uniform這個(gè)詞。
            如果只從源代碼(維基上的那份)入手uniform優(yōu)化的實(shí)質(zhì)就預(yù)處理一個(gè)delta,每次免去除法運(yùn)算。
            不管高爺爺是怎么一步一步想到這里來(lái)的(那本書我沒(méi)看……), 源代碼中已經(jīng)不怎么能體現(xiàn)出來(lái)了。


            但實(shí)際上,效果并不一定很顯著, 我瞎猜的啊。
            因?yàn)槌龜?shù)是2, 實(shí)際上并不需要做除法, 只是移位。

            -------- 注意 --------
            一次移位的寫法有(但不限于)以下2種:
            int m = lower + ( (upper - lower) >> 1);
            int m = lower + (upper - lower) / 2u; /* 這個(gè)u很關(guān)鍵 */

            第1種使用sar,第2種使用shr。 只要upper>=lower, sar,shr都是正確的。

            而樓主和飯中淹使用的代碼
            int m = lower + (upper - lower) /2;
            會(huì)被翻譯為3條指令。
            飯中淹的代碼還沒(méi)有考慮溢出的情況。

            -------- 注意完畢 --------


            那么,不使用uniform的方式, 每次計(jì)算middle的消耗就是:
            減法,移位,加法。
            lower, upper都是緩存在寄存器中的。
            具體可以見(jiàn)上一篇文章的評(píng)論中我發(fā)的匯編代碼。


            而uniform的方式:
            i += delta[++d];
            大概就是
            mov r1, delta[r2(d)*4+4]
            add r3(i),r1

            一次加法;一次內(nèi)存訪問(wèn);d需要多占用一個(gè)寄存器(否則更慢),++d還需要要一次加法。


            這個(gè)靜態(tài)存儲(chǔ)區(qū)的內(nèi)存訪問(wèn)很討厭。
            非uniform方式lower,upper所在頁(yè)面肯定是被加載的,因?yàn)樗鼈兪呛瘮?shù)參數(shù),處在棧頂。
            uniform的方式,delta就不一定在頁(yè)面中,有可能被置換出去了。這種情況一旦發(fā)生,說(shuō)不定效率就是數(shù)量級(jí)的下降,和非uniform完全沒(méi)得比。
            并且,這種情況,通過(guò)這種小的測(cè)試程序還不一定能測(cè)得出來(lái) —— 怎么模擬一個(gè)大的、長(zhǎng)期運(yùn)行的、偶爾調(diào)用二分查找的程序,它已經(jīng)將delta所在頁(yè)面置換出去了?

            從本質(zhì)上來(lái)說(shuō), uniform的方式的一個(gè)直接缺陷就是造成數(shù)據(jù)的局部性不如非uniform的方式。而且這缺陷是無(wú)法修正的。 缺陷造成的影響可能會(huì)被uniform的其他優(yōu)勢(shì)所掩蓋, 但缺陷始終存在。


            當(dāng)然, 上面全都是臆測(cè),臆測(cè)。 需要實(shí)際測(cè)試一下。
            我覺(jué)得只要非uniform的注意移位問(wèn)題, 效率應(yīng)該不會(huì)比uniform的差太多。即使不缺頁(yè),內(nèi)存訪問(wèn)也是比較傷的。
            一旦缺頁(yè),我覺(jué)得uniform就沒(méi)什么可比性的了。



            ps:
            關(guān)于兩種方式的對(duì)比:
            1. 增加happy path的效率,不管unfortunate的效率
            2. 兼顧所有情況的效率

            除了uniform和非uniform,我還想起一個(gè)……
            不知道hash表和紅黑樹(shù)算不算這種對(duì)比的例子之一……
            re: 二分查找學(xué)習(xí)札記 OwnWaterloo 2009-10-08 16:23
            @飯中淹
            2-way和3-way的對(duì)比,通常和輸入有關(guān)。
            3-way一旦命中,就可以提前退出循環(huán), 但每次循環(huán)要多一次compare。
            2-way總是執(zhí)行l(wèi)gN次比較后才退出循環(huán),再測(cè)試是否命中。
            嚴(yán)格計(jì)算需要概率統(tǒng)計(jì)知識(shí)。
            這種蠻力測(cè)試我也做過(guò), 3-way通常表現(xiàn)得比2-way更優(yōu)秀。


            但2-way有一個(gè)決定性的優(yōu)勢(shì),是3-way無(wú)法相比的。
            "存在相同鍵的有序區(qū)間中查找鍵屬于某個(gè)區(qū)間的所有值",對(duì)這種需求,2-way可以繼續(xù)保持O(lgN)的復(fù)雜度, 而3-way會(huì)退化至O(N)。


            stl中使用2-way而不是3-way,可能就是基于這種考慮吧 —— 即使在最壞情況下也要足夠好, 在最好的情況也不算壞。
            @溪流
            我out了……

            boost 已經(jīng)有 intrusive 容器的實(shí)現(xiàn)了……
            http://www.boost.org/doc/libs/1_35_0/doc/html/intrusive/intrusive_vs_nontrusive.html

            我這里的boost版本非常老…… 1.33…… 所以沒(méi)發(fā)現(xiàn)……
            上上面關(guān)于interface和concepts的比較, 是因前不久給一個(gè)項(xiàng)目經(jīng)理解釋ducking type和以函數(shù)為單位的抽象形式, 但沒(méi)成功……

            現(xiàn)在算是想得比較清楚了, 但好像沒(méi)表達(dá)清楚…… 那么多文字, 我自己都不愿意看第2遍……


            整理一下:

            對(duì)單個(gè)"契約"的設(shè)計(jì)而言, 通過(guò)interface或是concepts沒(méi)有什么區(qū)別。
            并且interface比concepts更廣為人知。
            所以當(dāng)時(shí)怎么都沒(méi)能說(shuō)服他……

            interface的劣勢(shì)(也就是concepts的優(yōu)勢(shì))體現(xiàn)在"如何拆解與組合契約"上。
            如何拆分,設(shè)計(jì)interface, 是門學(xué)問(wèn)。
            那幾個(gè)方法通常是應(yīng)該整體提供的?
            這就是interface的劣勢(shì)的本質(zhì) —— 它總是要將若干方法"打包"到一起才可以。
            極端情況, 只含有一個(gè)方法的interface也很常見(jiàn), Dispose所屬接口就是。

            interface更麻煩的地方在于組合。
            當(dāng)一個(gè)f需要若干種interface的功能時(shí), 要么必須將一些interface繼續(xù)"打包", 形成一個(gè)新的, 要么運(yùn)行時(shí)檢查。


            而concepts打從一開(kāi)始, 就僅僅依賴所需要的東西, 不需要"打包"這一過(guò)程。
            拆分和組合是自由的。
            從某種程度上來(lái)說(shuō), 這也容易造成晦澀的設(shè)計(jì)。
            但什么設(shè)計(jì)不是學(xué)問(wèn)呢…… interface的設(shè)計(jì)同樣需要經(jīng)驗(yàn)。


            concepts相比interface來(lái)說(shuō), 限制更少, 也更靈活。
            這種靈活性是本質(zhì)上的 —— 因?yàn)椴淮嬖?quot;打包"這一限制 ——是不以現(xiàn)實(shí)編程中是否需要這種程度的靈活性,以及這種靈活性是否會(huì)被人們普遍接受而轉(zhuǎn)移的。


            靈活的事物通常難以掌握, 如果現(xiàn)實(shí)編程中不需要, 人們不理解、不接受, 也只能算是當(dāng)前條件下的劣勢(shì) —— 對(duì)太多數(shù)人來(lái)說(shuō)晦澀、復(fù)雜, 無(wú)法被廣泛使用。
            如果哪天人們接受了呢?



            還有一點(diǎn), 上面說(shuō)的GP和OO、OOP概念太廣泛。 OO、OOP的定義到底是什么, 各有各的說(shuō)法。
            所以改為 concepts 和 interface( in java and C# )的比較, 比較準(zhǔn)確。
            并且某些其他支持OO的語(yǔ)言 —— 現(xiàn)在是個(gè)語(yǔ)言都要說(shuō)自己支持OO, 不支持的也要改為支持... ——中, 是不需要interface這種"契約打包器"的。



            樓主啊, 我自我感覺(jué)比較好的建議, 就只有建議你實(shí)現(xiàn)一套侵入式容器(包括樹(shù)式堆)而已, 既鍛煉了技巧, 又不陷入"重復(fù)發(fā)明輪子"。

            其他的, 都是自說(shuō)自話 …… 不必想太多-_-

            因?yàn)樽錾线@行了,沒(méi)什么時(shí)間總結(jié)平時(shí)的一些零散想法。
            如果是中學(xué)做習(xí)題, 會(huì)因?yàn)樽鲋鲋?就累了 …… 就轉(zhuǎn)而"總結(jié)一下吧,比單純做來(lái)得有效, 同時(shí)休息休息"。
            而寫代碼…… 是沒(méi)有累的感覺(jué)的…… 就會(huì)一直寫下去……
            只有在討論的時(shí)候, 才會(huì)想去總結(jié)一些東西。
            把你的blog評(píng)論區(qū)當(dāng)吐槽了…… sorry ...
            @溪流
            嗯嗯, 你說(shuō)得很對(duì)。

            假設(shè)多態(tài)的使用者與多態(tài)抽象之間的層面叫A, 還有多態(tài)提供者與多態(tài)抽象之間的層面叫B。
            OOP中的A和OB很相似, 只有B是新加的內(nèi)容。
            而GP中, 無(wú)論A、B, 都是新鮮內(nèi)容。

            所以這樣比較是不公平的~_~


            我至今沒(méi)有完全明白rbtree的實(shí)現(xiàn), 但不妨礙我使用map和set。
            對(duì)一般的內(nèi)建類型, 或者符合STL規(guī)范的值類型, 直接使用即可。
            如果需要放自定義類型,需要了解的就是key_compare和嚴(yán)格弱序。
            也不算多吧? OOP同樣離不開(kāi)比較準(zhǔn)則, 只是key_compare換成了ICompare。
            如果還想高級(jí)一些, 還可以去了解allocator, 那是篇幅更長(zhǎng)的一套規(guī)范。
            OOP沒(méi)見(jiàn)有這功能, 不比較了。

            boost中有一些庫(kù), 我知道實(shí)現(xiàn)原理, 但不知道對(duì)付各種編譯器的trick。
            還有一些隱約知道原理, 更多的是不知道。
            boost的源代碼我一份都沒(méi)看過(guò)(堅(jiān)持不下去…… 確實(shí)很丑陋, 但很多代碼都是為了對(duì)付不同編譯器而已)

            舉個(gè)例子, any。 即使沒(méi)看源代碼, 我也知道這個(gè)庫(kù)應(yīng)該在何時(shí)使用, 以及如何使用 —— 因?yàn)槲铱戳宋臋n…… 不多的。 interface也需要看文檔的吧?

            但就是這么一個(gè)小東西, 使得我可以通過(guò)一個(gè)參數(shù)傳遞任何東西。
            并且不再需要繼承自某個(gè)基類……

            "一切皆為Object" —— 我覺(jué)得這是很可笑的事情。
            Finder.find?
            p1.fuck(p2); 還是 p2.fuck(p1);? 3p呢?
            太可笑了……

            而且在java和C#中實(shí)現(xiàn)free function其實(shí)并不難, 只是它們固執(zhí), 不愿意加入而已。

            OOP根本就不是一種純粹的編程范式。 不結(jié)合其他范式, OOP根本就寫不出程序來(lái)。 不知道那些追求所謂"純OO"的家伙是怎么想的 ……
            在我眼里, OO只有oo analysis, oo design, oo programming只是oo design而已。 實(shí)現(xiàn)design時(shí), 是命令式的。
            至少對(duì)java, c#的OO來(lái)說(shuō)是如此。

            不是我一人這么說(shuō), 你還可以去看看《冒號(hào)課堂》。


            上面有點(diǎn)小錯(cuò)誤, 糾正一下:
            C/C++中本來(lái)就可以以單一參數(shù)傳遞任何東西……
            any 是使得這一作法類型安全而已。
            當(dāng)然, 既然使用C/C++寫代碼, 不同與那些保姆語(yǔ)言, 程序員自己就要對(duì)類型安全負(fù)責(zé)。 所以any通常是為了堵住那些對(duì)類型安全尤為重視的人的嘴而已。
            我還是更喜歡void* ...

            真不知道那些成天叫囂著類型安全, 卻視generic加入java是一種退步, 使得java不純粹的人是怎么想的……
            難道"不犯錯(cuò)", 比"犯了錯(cuò)總有人補(bǔ)救" 更重要?
            OO為什么被人吹捧?
            可以說(shuō), 是它開(kāi)創(chuàng)了一個(gè)歷史,人們開(kāi)始普遍采用 "對(duì)抽象的、多態(tài)的事物編程"。
             
            在那之前, 無(wú)論是OP還是OB, 都只能完成模塊化, 方便分工開(kāi)發(fā)與測(cè)試。很少使用多態(tài)技術(shù)。

            OB編程(OP就省了…… OP->OB依然能完成一定復(fù)用,但方式不同):
            void f_ob(T v) { v.f(); }
             
            f是針對(duì)一個(gè)具體的T進(jìn)行編程。
            f的行為和T的行為緊緊綁定在一起。
            f想要表現(xiàn)出不同行為, 必須要T表現(xiàn)出不同行為。
            但無(wú)論T如何改變,T和f都只具有一種行為,最后一次改變后具有的行為。
             

            OO編程:

            void f_oo(I& i) { i.f(); }
             
            f現(xiàn)在的行為, 僅僅與i所表現(xiàn)出的契約綁定在一起。
            而i可以有各種各樣的滿足契約的方式
            這樣的一大好處就是, 對(duì)不同的D : I, f可以被復(fù)用。
            得到這一好處的前提就是, D 滿足 I與f之間的契約。
             

            GP編程:
            template<typename T>
            void f_gp(T v) { v.f(); }
             
            同樣提供了多態(tài)行為:以不同的T帶入f, 就能得到不同的行為。
            使得f能被復(fù)用。STL中的組件, 大部分是通過(guò)這種方式被復(fù)用的。
            并且,STL組件之間, 也大部分是通過(guò)這種方式復(fù)用的。
             

            1.
            無(wú)論是GP還是OOP都是組合組件的方式。
            它們(典型地)通過(guò)concepts 和 interface來(lái)抽象出組件多態(tài)行為。
            對(duì)這種抽象事物編程得到的結(jié)果(函數(shù)/類模板,包),可以被復(fù)用(軟件工程一大目標(biāo))。
            -------- -------- -------- -------- -------- -------- -------- --------
             

            上面提到契約。
            無(wú)論是OP、OB、OO、GP, 組件間要能協(xié)同工作, 都是需要契約的。
             
            OP:
            free 可以接受空指針, 而printf 不行。
            前者是free對(duì)調(diào)用者定下的約束, 后者也是printf對(duì)調(diào)用者定下的約束
            —— 要使用我就必須這樣做。
             
            malloc 失敗返回空指針, 而new 拋異常, 否則返回可用動(dòng)態(tài)內(nèi)存。
            這是它們對(duì)調(diào)用者的承諾。
            —— 使用我, 你能得到什么。

            T* p = new T;
            if (!p)  這種代碼只在古董編譯器上才可能有意義。
             
            "加上NULL判斷更好" , 也只有"C++方言"的古董學(xué)者才說(shuō)得出來(lái)。
             
            new[] 要 delete[] , new 要 delete, 這也是契約。
            "因?yàn)閏har是基礎(chǔ)類型,所以可以new[] , delete", 只有不懂軟件契約的白癡學(xué)者才說(shuō)得出來(lái)。
             

            OB:
            要將CString s 轉(zhuǎn)換為 TCHAR*, 一定要用 s.GetBuffer  而不是 (TCHAR*)&s[0]
            CString 的約束。
             
            basic_string.c_str() 一定以'\0'結(jié)尾, 而data() 則不是。
            basic_string  的承諾。
             

            OOP:
            我用的OOP庫(kù)、框架不多……  舉不出什么例子。
            但它的契約和通常OB"非常類似" : 完成什么、需要調(diào)用什么、調(diào)用順序、 參數(shù)合法性。
             

            GP:
            GP總共有哪些契約形式, 我總結(jié)不出來(lái)。
            但至少有一條 —— 它不再對(duì)T有完全限定, 而只作最小限定
             

            還是上面的代碼:
            void f_oo(I& i ) { i.f(); }
            D d;
            f(d); // 要求 D : I

            template<typename T>
            void f_gp(T v) { v.f(); }
            要求  v.f(); 合乎語(yǔ)法 :比如, 它既可以是non-static member function, 也可以是static member function。
            并且僅僅要求這一點(diǎn)。
             
             
            2.
            契約是普遍存在的, 不僅僅是GP、 其他范式都有。
            它是合作與復(fù)用的前提。
            -------- -------- -------- -------- -------- -------- -------- --------
             
             
            3.
            為什么GP飽受爭(zhēng)議, 而OO沒(méi)有?
            我覺(jué)得, 是因?yàn)閺腛B到OO過(guò)度比較自然。
             
             
            要求一個(gè)I需要怎樣, 一個(gè)I需要使用者怎樣的時(shí)候,
            通常也會(huì)有類似的需求 —— 要求一個(gè)C怎樣, 一個(gè)C需要使用者怎樣。
            注意, 這只是 client 與 I, 以及 client 與 C之間的契約。
            在這個(gè)層面上, 不需要學(xué)習(xí)新的思想。
            而下面會(huì)提到OO引入的新的契約形式。
             

            而GP的契約形式, 都是一些全新的形式
            其中最主要的形式是: T 是否具有一個(gè)叫f的函數(shù)(操作符也可以理解為一種調(diào)用函數(shù)的方式)。
            在C++中, 還有必須有某個(gè)名字的嵌套類型, 通過(guò)T::f強(qiáng)制static member function等形式。

            OO比較流行、支持OO的語(yǔ)言比較多,是目前主流。
            C++,Java,C#的開(kāi)發(fā)者總共占了多少比例?
             
            GP支持的語(yǔ)言其實(shí)也多。
            但拋開(kāi)C++(或者算上C++用戶中理解GP的人)怎么都比不上上面3個(gè)巨頭。
             

            批評(píng)自己不熟悉的事物是不嚴(yán)謹(jǐn)?shù)摹?br>遇見(jiàn)STL編譯錯(cuò)誤, 要么就去學(xué)習(xí)GP的方式, 要么就拋棄STL。
            抱怨STL是種傻逼行為 —— 明明是自己不會(huì)用, 要怪只能怪自己。
             
             
            -------- -------- -------- -------- -------- -------- -------- --------
            4.
            如同GP、 OO同樣需要學(xué)習(xí)、 需要文檔、 否則會(huì)充滿陷阱。
             
            上面提到的client 與 I的契約形式通常和 clinet 與 C之間的形式相同。
            使得OO的一個(gè)方面可以"溫固"。
             
            而client 與 D之間的契約? 這個(gè)層面不"知新"是不行的。
            并且這個(gè)層面上的契約常常是出bug的地方 —— 因?yàn)檫@是語(yǔ)法檢查不了的, 必須有程序員自己去滿足語(yǔ)意。
             
            舉個(gè)例子 :
            看見(jiàn)一個(gè)虛函數(shù),它是否可以被覆蓋? 覆蓋它的實(shí)現(xiàn)是否需要同時(shí)調(diào)用基類實(shí)現(xiàn)?
            除非是約定俗成的一些情況, 比如 OnNotify、OnXXXEvent這種名字比較明顯。
            其他情況, 不去看文檔, 依然是不知道的。
             
             
            我剛剛就犯了一個(gè)錯(cuò)。
            python 中 繼承HTMLParser , 覆蓋__init__ 時(shí), 必須調(diào)用基類實(shí)現(xiàn)。
            我確實(shí)不知道構(gòu)造函數(shù)也可以被完全覆蓋……(原諒我…… 第1次使用python……)
            在C++中, 基類的構(gòu)造函數(shù)是無(wú)論如何都會(huì)被調(diào)用的。
             
             
            我沒(méi)有調(diào)用基類的__init__, 然后報(bào)了一個(gè)錯(cuò)。
            好在報(bào)錯(cuò)清晰, 源代碼的位置都有, 源代碼也可見(jiàn), 源代碼也清晰。問(wèn)題很容易就找出來(lái)了。
             
             
            如果
            4.1.
            它不是構(gòu)造函數(shù), 只是一個(gè)普通的虛函數(shù)?
            名字也看不出什么蹊蹺?
             
            4.2.
            文檔也沒(méi)有記載是否需要調(diào)用基類?
            (HTMLParser中的文檔沒(méi)有說(shuō)要調(diào)用__init__, 這應(yīng)該是python的慣例, 所以就沒(méi)有記載了)
            沒(méi)有嚴(yán)格的錯(cuò)誤檢查?
            沒(méi)有完整的、信息豐富的call stack trace?
            等發(fā)現(xiàn)錯(cuò)誤時(shí), 也許都離題萬(wàn)里了。
             
            4.3.
            沒(méi)有清晰的源代碼(其實(shí)到了要查看源代碼的時(shí)候, 已經(jīng)是……)?
             

            這些問(wèn)題在OO中同樣是存在的, 只是它沒(méi)引起編譯錯(cuò)誤, 或者說(shuō)編譯錯(cuò)誤比較明顯, 容易修改。
            而更多的語(yǔ)意檢查, OO中 client 與 D之間的層次, 依然要靠程序員的學(xué)識(shí) —— 主要是查閱文檔的習(xí)慣。

            對(duì)比GP
            4.1之前的4.0(OnNotify, OnXXXEvent之流), 同樣存在一些約定俗成。
             
            對(duì)4.1, 同樣是查文檔。
             
            對(duì)4.2, 沒(méi)有文檔同樣犯錯(cuò)。
            C++中, 一部分錯(cuò)誤可以提前到編譯時(shí)(C++只在持編譯時(shí)支持ducking type)
             
            對(duì)4.3
            同樣需要去查看源代碼。
             
            GP的代碼比OOP難看? 
            同上面, 只是因?yàn)?strong>不熟悉、不信任、不需要這種抽象方法, 這些契約的形式。
             

            STL中的契約形式其實(shí)并不多。
            [first,last) 左閉右開(kāi)區(qū)間;
            iterator的幾個(gè)種類;
            (adaptable)binary(unary)_function;boost只是將其提升到N-nary,還省去了adaptable的概念;
            相等、等價(jià)、嚴(yán)格弱序。
            多嗎?
             
            而且, 我不覺(jué)得為了比較要去實(shí)現(xiàn)一個(gè)IComparable 有何美感可言……
             
             
            -------- -------- -------- -------- -------- -------- -------- --------
            5. 這些新的契約形式、 抽象方式, 有沒(méi)有其優(yōu)勢(shì)
            當(dāng)然有 —— 最小依賴帶來(lái)的靈活性。
            上面也提到, GP只依賴于其需要的契約, 對(duì)其不需要的, 通通不關(guān)心。
             
             
            現(xiàn)在詳細(xì)解釋上面
            D d;
            f_oop(d); // D : I
             
            T v;
            f_gp(v);    // v.f
             
            為什么后者比前者靈活。因?yàn)楹笳咧灰蕾囁枰臇|西。
             
            5.1
            template<typename T>
            void f123_gp(T v) { v.f1(); v.f2(); v.f3(); }
             
            可以使用
            struct T123_1 { void f1(); void f2(); void f3(); }
            struct T123_2 { void f1(); void f2(); void f3(); }
             

            void f123_oop(I& i) { i.f1(); i.f2(); i.f3(); }
            必須有一個(gè)
            struct I { virtual void f1(); virtual void f2(); virtual void f3(); };
            然后是:
            D1 : I; D2 : I; ...
             
             
            5.2
            如果現(xiàn)在需要實(shí)現(xiàn)另一個(gè)函數(shù), 它對(duì)T的需求更少 :

            template<typename T>
            void f12_gp(T v) { v.f1(); v.f2(); }

            T123_1, T123_2 可以使用
             
            新增一個(gè):
            struct T12_1 { void f1(); void f2(); }
            依然可以使用
             

            OOP就必須重構(gòu)了:

            struct I12 { virtual void f1(); virtual void f2(); };
            struct I123 : I12 { virtual void f3(); }
            struct D12 : I12 {};
             
            5.3
            再看 :
            template<typename T>
            void f23_gp(T v) { v.f2(); v.f3(); }

            T123_1, T123_2 依然可以使用
            T12_1 不行。
             
            但新增的
            struct T23_1 { void f2(); void f3(); }; 可以使用
             
             
            OOP又必須重構(gòu):
            總體趨勢(shì)是這樣的, OOP需要極端靈活的時(shí)候, 就會(huì)變成這樣:
            一個(gè)接口, 一個(gè)函數(shù)。
            struct I1 { virutal void f1(); };
            struct I2 { virutal void f2(); };
            struct I3 { virutal void f3(); };
            現(xiàn)在接口設(shè)計(jì)是極端靈活了。

            但使用接口時(shí), 依然逃不過(guò)2種都不太優(yōu)雅的作法:
            1. 接口組合
            struct I12 : I1, I2;
            struct I23 : I2, I3;
            struct I31 : I3, I1;

            2. 接口查詢
            不組合出那些中間接口, 但運(yùn)行時(shí)作接口查詢:
            void f12(I1* i1) {
              i1->f1();
              if (I2* i2 = dynamic_cast<I2*>(i1) {
                 i2->f2();
              }
              else { 將一部分編譯時(shí)錯(cuò)誤留到了運(yùn)行時(shí)。 }
            }
            這不是故意找茬, 而是將STL中的iterator換個(gè)簡(jiǎn)單的形式來(lái)說(shuō)明而已。
             

            也許絕大部分情況下, 是不需要靈活到每個(gè)接口一個(gè)函數(shù), 而是一個(gè)接口3、4個(gè)相關(guān)的函數(shù)。通常它們會(huì)被一起使用。

            即使沒(méi)有上面如此極端, 假設(shè)IPerfect1、IPerfect2都是設(shè)計(jì)得十分合理的, 3、4個(gè)函數(shù)的接口, 通常這3、4個(gè)函數(shù)要么必須一起提供, 要么都不提供, 單獨(dú)提供是不符合語(yǔ)意的, 提供太多又是不夠靈活的。
            這需要經(jīng)驗(yàn), 相當(dāng)多的經(jīng)驗(yàn)。 但總是可以完成的事情。
             
            但組合接口, 依然是OOP的痛處。
            我記不清C#和Java中的interface是否繼承自多個(gè)interface。
            如果不行, 它們就可能需要運(yùn)行時(shí)接口查詢。
            而C++, 要在這種"組合接口"與接口查詢之前作一個(gè)選擇。
             
            反觀GP, 它一開(kāi)始就不是以接口為單位來(lái)提供抽象,而是按需而定。
            所以, 它既不需要仔細(xì)的拆分接口, 也不需要組合接口。
            STL中數(shù)據(jù)、容器、算法相互無(wú)關(guān)、可任意組合。
            應(yīng)該是前無(wú)古人的突破。
            后面有沒(méi)有來(lái)者? 上面已經(jīng)說(shuō)了, OOP要達(dá)到這種靈活性, 同樣也有其代價(jià)。
            并且, OOP代價(jià)體現(xiàn)在丑陋, 而不是難以理解。
             

            靈活的事物肯定比不那么靈活的事物難理解,抽象總比具體難理解。
            所以抽象出一個(gè)合理的、廣泛接受的語(yǔ)意很重要。

            * 就是解引用, ++ 就是前迭代, -- 就是后迭代。
            支持--就是雙向, 支持 + n 就是隨機(jī)。
             
            GP也不會(huì)胡亂發(fā)明一些語(yǔ)意不清晰的概念。
            window w;
            control c;
            w += c; 這種代碼在GP界同樣是收到批評(píng)的。
             
             
            最后, 軟件工程中, 是否真正需要靈活到如此程度, 以至于大部分人難以(或者不愿意去)理解的事物, 我就不知道了……
            顯示讓用戶知道么……
            有約定俗成
            再不成就文檔
            再不成…… 只能看源代碼了……

            好消息是, 違反concepts一般都錯(cuò)在編譯時(shí),不會(huì)將錯(cuò)誤留在運(yùn)行時(shí)……
            @溪流
            nullptr是《eff cpp》中提到的。

            對(duì)于int和指針的重載:
            void f(int );
            void f(void* );

            f(0); // f(int );
            f(NULL); // 如果是stddef.h 中的NULL, f(int );
            f(nullptr); // 書中提到的那個(gè)nullptr, 會(huì)選中 f(void* );

            如果還有另一種指針:

            void f(int* ); nullptr 會(huì)引起歧義。

            當(dāng)然, 最好是避免這種重載……

            @溪流
            這個(gè)…… 其實(shí)是個(gè)口味問(wèn)題 …… all fine.
            我比較懶…… 有現(xiàn)成的就喜歡用現(xiàn)成的…… so ……

            @溪流
            用stdint.h。 把這個(gè)棘手的工作交給編譯器提供商。
            并且, 你實(shí)現(xiàn)容器庫(kù)的時(shí)候, 需要DWORD么……


            > 只要他們的定義是兼容的,傳入的值就可以用。
            你要去檢查。 是否每個(gè)庫(kù)都是兼容的。

            另一種方式是只依賴于一處定義。比如stdint.h


            msvc沒(méi)有提供stdint.h, 因?yàn)樗恢С諧99。
            網(wǎng)上有一個(gè)stdint.h for msvc, 使用BSD3條款的許可證。

            或者自己寫:

            msvc 提供了 __intN擴(kuò)展。
            所以 intN_t uintN_t 很好處理
            int_leastN_t, 這個(gè)本來(lái)就不能假設(shè)它的寬度, 按字面來(lái)即可。

            uintptr_t, intptr_t 可以包含basetsd.h
            typedef INT_PTR intptr_t;
            typedef UINT_PTR uintptr_t;


            現(xiàn)在你代碼中依賴的就是:
            (u)intN_t, (u)int_fastN_t ,, (u)intptr_t, (u)intmax_t 等等。
            上面貼的代碼有問(wèn)題, 重新貼一下……
             
            template<typename T,  >
            class vector {
                T
            * first_;
                T
            * last_;
            public:
                template
            <class ForIt>
                vector(ForIt first,ForIt last) {
                    difference_type d 
            = distance(first,last);
                    first_ 
            = new T[ d + padding ];
                    last_ 
            = first_ + d;
                    
            for (T* p = first;first!=last;++first,++p)
                        
            *= *first;
                }
            };
            不叫"跨容器"的iterator。
            而是iterator符合特定的concepts。
            不過(guò)你說(shuō)對(duì)了, 它確實(shí)和模板有關(guān)。
             
            比如,使用一對(duì)forwarding_iterator構(gòu)造vector的偽代碼:
            concepts
             
            vector有一個(gè)構(gòu)造函數(shù)模板。
            只要FotIt 滿足forwarding_iterator的概念,上面的構(gòu)造函數(shù)就可以生成。
            forwarding_iterator 要支持(但不限于)++, *, 因?yàn)樯厦娴拇a使用了這2個(gè)操作符。
            vector的實(shí)現(xiàn)也不會(huì)是上面那樣, 不會(huì)調(diào)用new 去默認(rèn)構(gòu)造一次。
            會(huì)使用其allocator來(lái)分配,并使用uninitialized_copy。
            不過(guò)那樣就看不到vector() 對(duì)forwarding_iterator的要求了。
             
             
            這是C++中的方式,GP的,基于concepts的方式。C#中可是沒(méi)得這么爽的哦。
            并且, GP的、基于concepts的方式, 也可以退化成OOP的,基于interface的方式。
            反之不行, 因?yàn)閏oncepts的偶和性更低。
             
             
            不僅僅是容器。 整個(gè)STL中的組件都是通過(guò)concepts來(lái)協(xié)作而非interface。
            如果你喜歡struct Iterator的方式, 或者IComparable , 也可以這么搞一套。
            代碼不會(huì)比GP來(lái)得少, 效率不會(huì)比GP來(lái)得高, 也不會(huì)比GP靈活 —— GP可以退化為基于interface, 反之不行。
             
            const VOID *NULL = 0;
            在C++中, 這樣定義NULL是行不通的。
             
            普遍的做法是直接使用0,或者stddef.h, cstddef 中的NULL宏:
            #ifdef __cplusplus
            #define NULL 0
            #else
            #define NULL ((void*)0) /* 這是C中的做法 */
            #endif
             
            void* 在C中可以隱式轉(zhuǎn)換到其他類型指針。
            但C++不行。
             
             
            或者, 更激進(jìn)一點(diǎn), 使用nullptr:
            nullptr
            因?yàn)镃++0x將引入nullptr作為關(guān)鍵字。
            使用nullptr,算是"向前兼容"吧…… 等轉(zhuǎn)到C++0x時(shí),就可以直接使用真正的nullptr了。
             
            上面最后一種nullptr和C++0x中的語(yǔ)意最相似, 不過(guò)不一定能在所有編譯器中都通過(guò)。
            至少要支持成員函數(shù)模板才行。
            如果不支持匿名類可以改用別的方式實(shí)現(xiàn)。
             
             
             
             typedef struct interface;
            這是什么語(yǔ)法?
            這也是徒增概念的地方。
             
            C++程序員如果不知道下面的代碼是一個(gè)interface
            struct Ixxx {
                virtual ~Ixxx();
                virtual ...
            };
             
            將struct 換成interface對(duì)他的幫助也不大。
            @溪流

            各種現(xiàn)有的流行的庫(kù)中, 實(shí)現(xiàn)了侵入式容器的比較少。
            大都是非侵入式容器。

            侵入式容器我只在如下一些庫(kù)中見(jiàn)到過(guò):
            linux/list.h, linux/rbtree.h
            sourceforge有個(gè)叫l(wèi)ibaasdl的項(xiàng)目,有一個(gè)GDST(generic data-structure template)子庫(kù)中的一些是侵入式的(還有一些我沒(méi)看完)

            SGI-STL中4種關(guān)聯(lián)容器底層使用的是同一種容器,_Rb_tree。
            它是非侵入式的。
            但是構(gòu)建它的_Rb_tree_base、_Rb_tree_base_iterator都是非侵入式的。
            SGI-STL沒(méi)有構(gòu)建出一個(gè)侵入式的RbTree層, 而是直接使用侵入式的_Rb_tree_base,和_Rb_tree_base_iterator構(gòu)建出了非侵入式的_Rb_tree。
            稍微有點(diǎn)可惜。
            不過(guò)即使有一個(gè)侵入式的RbTree, SGI-STL也是不會(huì)將其公開(kāi)出來(lái)的。


            如果想練手, 可以考慮構(gòu)建一套侵入式的容器, 比僅僅重復(fù)STL中的東西來(lái)得有意義。

            還有, STL中沒(méi)有樹(shù)式的堆。
            heap相關(guān)的那幾個(gè)函數(shù)都是對(duì)random_access的線性表使用的。
            也可以考慮在這里做做文章。
            @溪流
            2:不再加前綴

            其實(shí)vczh同學(xué)放出來(lái)的代碼中的VL_前綴……
            我覺(jué)得看上去是很傷眼的……
            當(dāng)然, 這是口味問(wèn)題。


            關(guān)于這個(gè)問(wèn)題:
            "但是,問(wèn)題是,當(dāng) using namespace xl 并 #include <Windows.h> 以后,隨手寫一個(gè)“DWORD”,就會(huì)有歧義了。如果用的時(shí)候要寫成 xl::DWORD,那還不如現(xiàn)在就命名成 XL_DWORD 好了"

            首先, 不必定義xl::DWORD。
            對(duì)于其他情況, 名字空間相對(duì)于前綴是有一些優(yōu)勢(shì)的。
            1. 如果存在歧義, 無(wú)論是名字空間,還是前綴, 都必須使用全稱。
            2. 不存在歧義的時(shí)候, 名字空間可以打開(kāi), 可以別名。 而前綴必須時(shí)刻都帶著, 永遠(yuǎn)逃不掉。
            3. 如果不小心, 兩個(gè)使用前綴的庫(kù)都出現(xiàn)了相同名字, 前綴技術(shù)就很麻煩。
            A庫(kù)是個(gè)做網(wǎng)絡(luò)的, 有一個(gè) net_f,
            B庫(kù)也是個(gè)做網(wǎng)絡(luò)的, 依然一個(gè) net_f,
            要同時(shí)使用兩個(gè)庫(kù), 就需要自己去寫轉(zhuǎn)發(fā)函數(shù)。

            而名字空間, 可以一開(kāi)始就很長(zhǎng)。
            namespace net_byA { void f() }
            namespace net_byB { void f() }


            只使用A(或B)的用戶,就可以使用別名:
            namepsace net = net_byA(B);
            net::f;

            如果同時(shí)使用A、B的用戶, 只好:
            net_byA::f;
            net_byB::f;

            也比自己寫轉(zhuǎn)發(fā)函數(shù)要強(qiáng)。

            總之, 名字空間是一種更靈活的方式。

            如果決定使用C++編寫庫(kù), 而且不考慮過(guò)分古董的編譯器, 就應(yīng)該選用名字空間。
            @溪流
            1. 如非必要,不typedef基礎(chǔ)類型

            為什么要typedef?
            typedef是為了提供語(yǔ)意。

            DWORD, WORD, BYTE的語(yǔ)意是固定長(zhǎng)度整數(shù)。
            stdint.h, cstdint, sys/stdin.h, windows.h 中已經(jīng)有這樣的功能, 就盡可能去復(fù)用。
            自己省事 —— 你有精力去為不同平臺(tái)定義出對(duì)應(yīng)的DWORD, WORD, BYTE嗎?
            既然上面的幾個(gè)文件中已經(jīng)給出了這種功能, 并且在各個(gè)平臺(tái)下都測(cè)試過(guò), 為什么不直接使用呢?

            別人也省事。
            需要明白一點(diǎn):用戶與其依賴的庫(kù)通常不是線性結(jié)構(gòu),而是樹(shù)形結(jié)構(gòu)。
            A庫(kù)為了可移植性的去typedef DWORD, WORD, BYTE,
            B庫(kù)也為了可移植性去typedef DWORD, WORD, BYTE,
            一個(gè)客戶C, 同時(shí)需要A、B, 他該用哪個(gè)庫(kù)的DWORD?
            這種作法是徒增不必要的概念。


            對(duì)自己庫(kù)獨(dú)有, 并有可能改變的概念, 才可以考慮使用typedef 來(lái)建立一個(gè)語(yǔ)意。
            比如time.h中的time_t, clock_t, 代表了兩個(gè)獨(dú)立的概念。
            @唐僧
            高爺爺還用匯編實(shí)現(xiàn)算法的……
            他別那么牛好不好……
             
            我貼一下上面的c代碼用gcc生成的匯編代碼吧,不過(guò)是intel格式的,因?yàn)閍t&t格式的匯編我看不懂……
            .globl _binary_search
                .def    _binary_search;    .scl    
            2;    .type    32;    .endef
                                           
            _binary_search:               
                push    ebp                    
                mov    ebp, esp
                mov    edx, DWORD PTR [ebp
            +16]     ;edx = lower
                push    esi                    
                mov    ecx, DWORD PTR [ebp
            +20]     ;ecx = upper
                mov    esi, DWORD PTR [ebp
            +8]      ;esi = keys
                push    ebx                     
                mov    ebx, DWORD PTR [ebp
            +12]     ;ebx = target
                .p2align 
            4,,15
            L8:
                cmp    edx, ecx                    ;
            if (lower!=upper)
                je    L2
            L10:
                mov    eax, ecx                    ;middle 
            = eax = ecx = upper
                sub    eax, edx                    ;eax 
            -= edx, eax = upper-lower
                shr    eax                         ;eax 
            /= 2
                add    eax, edx                    ;eax 
            += edx, eax = lower + (upper-lower)/2u
                cmp    DWORD PTR [esi
            +eax*4], ebx  ;if (keys[middle] < target)
                jge    L3
                lea    edx, [eax
            +1]                ;lower(edx) = middle(eax) + 1
                cmp    edx, ecx                    ;
            if ( lower!=upper)
                jne    L10                         ;(keys,target,middle
            +1,upper)
            L2:
                pop    ebx
                mov    eax, edx                    ;
            return lower
                pop    esi
                pop    ebp
                ret
                .p2align 
            4,,7
            L3:
                mov    ecx, eax                    ;upper(ecx)
            =middle
                jmp    L8                          ;f(keys,targer,lower,middle)

            迭代生成的匯編代碼僅僅是標(biāo)號(hào)取了不同的名字而已……
             
             
            理論上是的, 因?yàn)槔碚撋弦部梢赞D(zhuǎn)為迭代的吧。
            實(shí)際上,寫出為尾遞歸形式就是需要引入一些額外參數(shù)作為計(jì)算時(shí)的變量。
            只要能寫出尾遞歸形式,手動(dòng)轉(zhuǎn)迭代都不難了。
             
            比如:
            int factorial(int x) { return x>2? x*factorial(x-1): 1; }

            不是一個(gè)尾遞歸, 因?yàn)閒actorial中對(duì)factorial調(diào)用后,需要取得結(jié)果并與x相乘才能返回。
             
            轉(zhuǎn)化為尾遞歸形式,需要引入一個(gè)額外參數(shù):
            int factorial_tail_recursion(int x,int acc) {
                
            return x>2? factorial_tail_recursion(x-1,acc*x): acc;
            }

            int factorial(int x) { return factorial_tail_recursion(x,1); }

             

            而factorial_tail_recursion“手動(dòng)”轉(zhuǎn)迭代都不是難事了:
            int factorial_iteration(int x,int acc) {
                
            while (x>2)
                    acc 
            *=x , --x;
                
            return acc;
            }
            int factorial(int x) { return factorial_tail_recursion(x,1); }

            再把2個(gè)函數(shù)合二為一, 始終以1作為累積的初始值:
            int factorial_iteration_final(int x) {
                
            int acc = 1;
                
            while (x>2)
                    acc 
            *=x , --x;
                
            return acc;
            }

             
            還是比較形式化的。 證明估計(jì)要留給vczh來(lái)做了。
            @唐僧
            gcc確實(shí)牛逼…… 怎么會(huì)有這種怪物……
            對(duì)C/C++新標(biāo)準(zhǔn)支持很快……
            compiler collection... 不知道它對(duì)非C/C++語(yǔ)言支持得怎樣。
            還支持這么多平臺(tái)……


            如果函數(shù)f返回前的最后一個(gè)步驟是調(diào)用另一個(gè)函數(shù)g,也就說(shuō)g返回f后,f確實(shí)無(wú)事可做,再返回f的調(diào)用者;
            那么,從理論上說(shuō),總是可以優(yōu)化為:被調(diào)用函數(shù)g不再返回調(diào)用函數(shù)f,而直接返回f的調(diào)用者。
            這樣就可以在g中"復(fù)用"f的所使用棧資源。
            這被稱為尾調(diào)用。
            http://en.wikipedia.org/wiki/Tail_call

            如果尾調(diào)用恰好是調(diào)用的自身,就是尾遞歸(調(diào)用)。連使用的參數(shù)數(shù)量都是相同的…… 應(yīng)該說(shuō)是比較容易優(yōu)化的。
            并且,如果能將遞歸轉(zhuǎn)換為尾遞歸形式, 通常"手動(dòng)"轉(zhuǎn)化為迭代形式就很簡(jiǎn)單了……


            上面的遞歸代碼符合尾調(diào)用。 我只是想驗(yàn)證一下是否真的會(huì)被編譯器優(yōu)化。
            其實(shí)我預(yù)想是不行的,結(jié)果…… 還真的可以……
            這樣就有了一個(gè)理由:如果出于演示的目的,如果遞歸比迭代形式更優(yōu)美,就不提供迭代形式了 —— 轉(zhuǎn)迭代留作練習(xí)、或者換gcc ~_~
            @唐僧
            看來(lái)就我是夜貓了……
            說(shuō)點(diǎn)題外話……
            因?yàn)樯厦娼o出的是遞歸代碼, 所以就稍微查看了一下各種編譯器的優(yōu)化情況, 有點(diǎn)吃驚……
            使用遞歸求階乘的代碼,msvc8,9; gcc 3.4.x可以優(yōu)化為迭代,vc6不行。
            對(duì)上面提到的二分搜索的遞歸形式:
            tail_recursion

            gcc也能將為遞歸優(yōu)化為迭代。
            下面是一個(gè)轉(zhuǎn)化為迭代的版本:
            iteration

            gcc對(duì)這兩者生成的代碼,除了標(biāo)號(hào)不同,其他地方完全一樣……
            msvc系列就不行了…… 老老實(shí)實(shí)的遞歸了……
             
            re: Collection::List OwnWaterloo 2009-09-23 18:35
            @陳梓瀚(vczh)
            支持推倒……
            re: Collection::List OwnWaterloo 2009-09-23 14:56
            如果以GP而非OOP的方式構(gòu)建集合,你會(huì)發(fā)現(xiàn)C++的template即使沒(méi)有delegate也會(huì)很爽。C#沒(méi)得這么爽。
            @唐僧
            再回一貼 ……

            Uniform binary search中需要的那個(gè)table —— delta, 好像可以用template meta programming在編譯時(shí)計(jì)算完畢 ……
            C++太強(qiáng)大了~_~

            否則, 如果運(yùn)行時(shí)計(jì)算的話:
            1. 將make_delta的工作留給client(就像鏈接中的示例代碼一樣)
            會(huì)使得unisearch不太好用。多次調(diào)用make_delta雖然不會(huì)錯(cuò), 但賠本了。
            如果只調(diào)用一次, 時(shí)機(jī)不好掌握。
            如果在main中, main前的執(zhí)行代碼不能使用unisearch, 而且如果每個(gè)庫(kù)都要求在main中這么初始化一次, main會(huì)受不了的……

            2. unisearch自己處理好make_delta
            那每次對(duì)unisearch的調(diào)用,會(huì)有一次額外的運(yùn)行時(shí)檢測(cè) if (!is_init) 是逃不掉了。
            @唐僧

            哦 ……

            int a[] = { 7, 3, 5, 7, 2 }; 用Uniform binary search可以如下處理……
            binary_search(keys=a+1, target=5, count=3 );

            快天亮了, 頭有點(diǎn)暈……
            @唐僧
            4點(diǎn)58分的回復(fù)……

            The art of computer programming …… 一直沒(méi)下決心去啃……

            這個(gè)Uniform binary search是不是將除法操作緩存一下?
            其實(shí)聰明點(diǎn)的編譯器同樣可以將
            i /= 2u; 優(yōu)化成 shr i
            它使用一個(gè)預(yù)先計(jì)算好的middle值的緩存, 即使真能提高一點(diǎn)速度, 但能處理這種情況嗎?
            int a[] = { 7, 3, 5, 7, 2 }; 整個(gè)a無(wú)序, 但a[1] - a[3]升序。
            binary_search(keys=a, target=5, lower=1,upper=3 );


            確實(shí)two-way不好寫。 12點(diǎn)多的時(shí)候就去問(wèn)了一個(gè)拿過(guò)2次ACM金牌的室友,讓他寫一個(gè)二分搜索。
            他回答“讓我想一想”時(shí), 我還有點(diǎn)驚喜 —— 之前我也問(wèn)過(guò)一些人, 但終于有一個(gè)不是張口(隨手)就給出代碼的人了, 大牛果然不同凡響。
            等了一會(huì)后, 得到一個(gè)3-way的……

            打算將數(shù)據(jù)結(jié)構(gòu)重新學(xué)一遍…… 透徹理解一下……
            以前學(xué)的那叫啥…… 所以嘛, 只能看著室友拿金牌-_-。

            @唐僧
            謝謝~~

            我想到一個(gè)證明3-way不可能實(shí)現(xiàn)確定取向的方法。 其實(shí)非常簡(jiǎn)單……

            因?yàn)橐粋€(gè)binary-search算法,必須對(duì)所有輸入數(shù)據(jù)都有確定的取向,才能說(shuō)該算法有確定取向。
            故證明某個(gè)binary-search算法不具有確定取向只要構(gòu)造出一個(gè)反例,即可……

            比如…… 一個(gè)極端的數(shù)據(jù):鍵全部相同。 3-way肯定是第1次就退出查找了, 肯定不具有確定取向了。

            @陳梓瀚(vczh)
            你再次偏題。怎么又扯上列表了?

            多重值的map? 比如std::multimap?
            如果使用的是std::multimap, 我相信用得更多的也是lower_bound、upper_bound、equal_range, 而不是find。
            同樣說(shuō)明了對(duì)存在相等鍵的序列,查找的取向是有用的。


            如果需要一個(gè)僅僅構(gòu)建一次,查找多次,并且含有等值鍵的序列,并不一定需要multimap。
            std::lower_bound,std::upper_bound同樣可以對(duì)數(shù)組或者list查找,僅僅需要序列有序,并不要求序列有其他什么特性, 列表的負(fù)擔(dān)又從何說(shuō)起?


            如果待查找的序列有相等的鍵, 那么查找算法有一定的取向就是一個(gè)有用的需求。跟序列是array、list還是map無(wú)關(guān)。

            @陳梓瀚(vczh)
            這跟singleton有什么關(guān)系?
            @那誰(shuí)
            第2版還是第1版的編程珠璣? 書中有證明取向性么?
            取向其實(shí)也只算個(gè)附加需求。 只對(duì)多值才有用……

            我不知道three-way如何寫出固定取向……
            只考慮過(guò)two-way的幾種區(qū)間劃分的取向。
            期待你的研究成果~~~

            @陳梓瀚(vczh)
            存在相同鍵的有序序列中,查找取向是有用的。
            它能解決:“找鍵等于k的所有值”,“找鍵大于等于p,小于等于q的所有值”, 這種常見(jiàn)的需求。
            @唐僧
            編程珠璣上有講這個(gè)? 第2版中? 第1版已出版很久、很多細(xì)節(jié)記不清了……
            wiki是指編程編程珠璣的wiki? 給個(gè)鏈接撒~~~

            我第1次發(fā)現(xiàn)這個(gè)問(wèn)題是在看《代碼之美》時(shí),當(dāng)時(shí)困惑了很久“難道我以前不是這樣寫的?”。我的確是寫成three-way了。
            確實(shí), 如果three-way是聽(tīng)說(shuō)二分查找思想后,就能立即編碼的話, two-way就需要深入考究考究才能編寫出來(lái)。


            two-way就能有明確的取向啊。
            對(duì)區(qū)間[lower,upper]:
            1. 如果取中值 m = (lower+upper)/2
            將區(qū)間劃分為[lower,m],[m+1,upper]
            直到區(qū)間只有一個(gè)元素:[lower,lower]
            取向就是從lower到upper, 第1個(gè)大于等于target的index。

            2. 如果取中值 m = (lower+upper+1)/2
            將區(qū)間劃分為[lower,m-1],[m,upper]
            直到區(qū)間只有一個(gè)元素:[lower,lower]
            取向就是從upper到lower,第1個(gè)小于等于target的index。

            上面給出了一份代碼的鏈接, 我覺(jué)得挺優(yōu)雅的……
            @陳梓瀚(vczh)
            沒(méi)明白。 這個(gè)條件能保證什么? 編譯時(shí)的依賴關(guān)系?
            boost夠機(jī)靈的, 避重就輕。


            singleton可能會(huì)遇到的問(wèn)題, 以及如何解決, 最詳盡的資料在《modern c++ design》中有介紹。 問(wèn)題大致有如下幾個(gè)方面:
            1. 限制實(shí)例數(shù)量
            2. singleton相互引用
            3. dead-reference
            4. 線程安全


            C++中:
            singleton這種模式能"直接"解決的問(wèn)題只有1;利用C++語(yǔ)言機(jī)制 —— private —— 來(lái)限制實(shí)例數(shù)量。
            而問(wèn)題1也是眾多文章討論得最多的, 簡(jiǎn)單嘛……

            解決1問(wèn)題,并不一定需要singlet這種模式;而其他問(wèn)題又不是singleton這種"模式"本身能解決的。
            綜上, singleton in cplusplus is bullshit ……



            boost這種實(shí)現(xiàn),只能解決1, 在一定條件下,能解決4。
            如果遇見(jiàn)2、3, 同樣完蛋。

            boost.singleton解決4,需要滿足如下條件:
            "The classes below support usage of singletons, including use in program startup/shutdown code, AS LONG AS there is only one thread running before main() begins, and only one thread running after main() exits."
            如果這種條件能被滿足, 直接使用全局變量同樣是線程安全的。

            如果真的需要解決問(wèn)題1,以及難看的全局變量:
            可以將這個(gè)全局變量以及類的定義放在單一翻譯單元中,并在該翻譯但與實(shí)現(xiàn)若干wrapper函數(shù)即可。

            同樣, 對(duì)于問(wèn)題2和3, 全局變量也通常會(huì)壞事。

            綜上, boost.singleton, 簡(jiǎn)直就是為模式而模式。
            @那誰(shuí)
            cpp的rss比較快。。。


            這里有一份代碼:
            http://www.cnblogs.com/Devfly/archive/2009/09/18/can-you-write-a-right-binary-search-algorithm.html#1651172

            是將閉區(qū)間[lower,upper], 取m = (lower + upper)/2
            分為2個(gè)區(qū)間[lower,m] ; [m+1,upper]

            遞歸邊界是區(qū)間只含一個(gè)元素: [lower,lower]

            取向是返回[lower,upper]中大于等于target的index。
            遞歸邊界一定能達(dá)到很好證明, 取向比較麻煩。


            而其他一些常見(jiàn)的區(qū)間取法, 比如[first,last),
            還有中值的取法,比如 (lower + upper + 1)/2, 或者用那個(gè)什么黃金分割……
            以及多值時(shí)的取向, 隨機(jī), 第1個(gè), 最后1個(gè)。
            它們與stl中l(wèi)ower_bound, upper_bound的關(guān)系
            等等…… 都挺有意思的……
            不過(guò)最近有其他事要忙…… 只好終止了……

            你感興趣的話可以研究研究, 我就直接撿個(gè)現(xiàn)成了~~~
            因?yàn)槎植檎业乃枷牒芎?jiǎn)單, 很多人稍微看看就開(kāi)始編碼了, 沒(méi)有考慮:
            1. 每次遞歸中,區(qū)間如何劃分
            2. 遞歸的邊界有哪些,是否能達(dá)到
            3. 查找的值存在多個(gè)時(shí), 將取得哪一個(gè)

            仔細(xì)推敲邊界的人不多。 大多都是隨手寫寫, 最多加幾個(gè)測(cè)試數(shù)據(jù)。
            區(qū)間劃分, 我只在少數(shù)幾個(gè)地方看到是被“二分”, 普遍做法是“三分”。
            少數(shù)幾個(gè)地方是《代碼之美》;cplusplus網(wǎng)站中l(wèi)ower_bound,upper_bound的偽代碼。
            討論多值取向的文章就更少了。
            > 原來(lái)如果把這個(gè)十進(jìn)制數(shù)考慮成2進(jìn)制
            在C/C++中,整數(shù)本來(lái)就是按2進(jìn)制而不是按10進(jìn)制存儲(chǔ)的。
            不存在考慮成2進(jìn)制的說(shuō)法。

            > 突然想起了將10進(jìn)制轉(zhuǎn)化成2進(jìn)制的方法
            10進(jìn)制是表象, 2進(jìn)制才是本質(zhì)。
            10進(jìn)制只存在于輸入輸出的過(guò)程中, 變量最終是按2進(jìn)制存儲(chǔ)。



            > 右移一位相當(dāng)于除以2,模除2就是把最后那一位給取出來(lái)了
            > 不斷的模除2,就把這個(gè)2進(jìn)制數(shù)一位一位的取出。
            int i,d;
            d = i % 2u;
            i /= 2u;

            如果你使用的編譯器不是古董,第2、3行代碼也會(huì)分別被編譯為位與、移位—— 不一定真的需要寫為 & , >>= —— 而不是除法。

            re: std 容器 assign的注意之處 OwnWaterloo 2009-09-17 17:18
            需要注意的是(int*)buf這個(gè)轉(zhuǎn)型,而不是assign。
            每寫下一個(gè)轉(zhuǎn)型時(shí),問(wèn)問(wèn)自己“我到底在干什么”。
            re: 關(guān)于while(cin) OwnWaterloo 2009-09-06 19:06
            >> 控制結(jié)構(gòu)中的布爾條件值并不是非得直接轉(zhuǎn)換為bool不可,只要能夠轉(zhuǎn)換為某個(gè)整數(shù)型別或指針型別就夠了。

            選void* 而不是整數(shù)類型是有原因的。 可以避免如下代碼被編譯通過(guò):
            cin<< xxx;


            streambuf是重點(diǎn)之一。
            istream 負(fù)責(zé)格式化輸入, ostream負(fù)責(zé)格式化輸出。
            streambuf 如其名那樣, 作為格式化結(jié)果與最終輸出目的地之間的緩存。

            而stringstream, fstream, 沒(méi)有太多的功能。
            格式化功能是繼承自iostream。
            而它們使用的是stringbuf, filebuf, 是streambuf的子類, 提供一些額外功能。
            stringstream, fstream比iostream多的功能, 正是其buf提供的。
            目前C++是沒(méi)有region 這種東西的, C++0x也沒(méi)聽(tīng)說(shuō)有這么個(gè)東西。
            這是msvc提供的一個(gè)擴(kuò)展。

            你自己也試過(guò)vs2003了。


            這是gcc編譯的情況:
            warning: ignoring #pragma region name
            warning: ignoring #pragma endregion comment
            warning: ignoring #pragma region Region_1
            warning: ignoring #pragma endregion Region_1


            這是vc6編譯的情況:
            warning C4068: unknown pragma
            warning C4068: unknown pragma
            warning C4068: unknown pragma
            warning C4068: unknown pragma

            re: 一個(gè)有趣的小問(wèn)題 OwnWaterloo 2009-09-01 23:11
            @莫失莫忘
            > OwnWaterloo,你是不是一直等在CPP BLOG上?

            有個(gè)"有回復(fù)時(shí)郵件通知我"。
            我通常都開(kāi)著郵箱, so ……


            > 在JAVA中,通過(guò)無(wú)效的地址去訪問(wèn)是絕對(duì)會(huì)出錯(cuò)的
            java中不能隨意操作指針。 除了null, 如何能產(chǎn)生一個(gè)無(wú)效地址?
            array 都是一個(gè)所謂的"對(duì)象", 記錄著大小, 隨時(shí)可以檢查邊界。
            JNI可以嗎?


            > 這只是常人的一個(gè)思維:通過(guò)不存在的去訪問(wèn)當(dāng)然會(huì)出錯(cuò)
            這是javaer的思維。

            現(xiàn)在使用的計(jì)算機(jī)中, 馮諾依曼架構(gòu)是主流。
            所以, 當(dāng)cpu真正進(jìn)行運(yùn)算的時(shí)候, 一切都是地址。

            硬件會(huì)提供一些手段, 區(qū)分一些地址是否有效。
            但通常是軟件, 通過(guò)"指令流"而產(chǎn)生"控制流", 來(lái)判斷某地址是否有效。


            C/C++不提供這些保姆功能。
            這個(gè)例子充分說(shuō)明了,正因?yàn)闆](méi)有這些保姆功能, C/C++中, 程序員要尤其小心, 不能依賴于"編譯器、OS、CPU"提供的"功能有限、可有可無(wú)(標(biāo)準(zhǔn)未定義)"的保護(hù)手段, 而要自己注意越界問(wèn)題。
            re: 一個(gè)有趣的小問(wèn)題 OwnWaterloo 2009-09-01 22:58
            @莫失莫忘
            > 語(yǔ)意上的解引用?實(shí)際上的解應(yīng)用?

            這真的不是搞笑。

            S sa[12];
            int ia[12]
            那么sa[100]; ia[26]; 這2
            個(gè)表達(dá)式, 按C++的說(shuō)法, 其結(jié)果類型就是 S& 和 i&。

            如果給一些C程序員看, 他們還會(huì)告訴你,
            sa[100]; 等效于 *(sa+100);
            ia[26]; 等效于 *(ia+26);

            所以, 我說(shuō)這里有一個(gè)語(yǔ)意上的解引用。


            但實(shí)際在, 在i386下, msvc和gcc編譯器, 都不會(huì)理睬
            sa[100]; ia[26]; 這么一個(gè)孤單的表達(dá)式。

            只有當(dāng)
            int i = ia[26]; /* int i = *(ia+26); */ 讀
            ia[26] = 1212; /* *(ia+26) = 1212 */ 寫
            的時(shí)候, 才會(huì)真正產(chǎn)生"解引用"的代碼 —— mov指令

            并且, 只有 int* p = ia + 26; p的地址沒(méi)有相應(yīng)權(quán)限(將未提交也理解為缺陷缺失的話), 才會(huì)引發(fā)錯(cuò)誤。


            對(duì)自定義類型:
            sa[100].a(); 調(diào)用處都不會(huì)產(chǎn)生解引用代碼。

            只有在
            S::a() { // 定義處
            才有可能產(chǎn)生解引用代碼—— 如果操作非靜態(tài)數(shù)據(jù)成員的話。
            }


            按C++標(biāo)準(zhǔn)來(lái)說(shuō), 表達(dá)式:
            sa[100]; ia[26];
            已經(jīng)是未定義行為了。 只是在i386上, 通常不會(huì)造成什么問(wèn)題。

            又因?yàn)镾::a()沒(méi)有操作非靜態(tài)數(shù)據(jù)成員, 就不會(huì)產(chǎn)生實(shí)際的mov指令, 也不會(huì)有問(wèn)題。


            -----------------------------------------
            sum是S的靜態(tài)數(shù)據(jù)成員, sum本來(lái)就是在靜態(tài)存儲(chǔ)區(qū)中的。
            即使是按C++標(biāo)準(zhǔn), 無(wú)論是
            S::sum
            this->sum

            都是對(duì)S::sum——靜態(tài)存儲(chǔ)區(qū)中的一個(gè)變量——的操作。
            沒(méi)有問(wèn)題是自然的。

            for (int i=0;i<1212;++i) sa[i].sum
            只要 sa[i]表達(dá)式不出問(wèn)題, sa[i].sum 依然是訪問(wèn)的S::sum , 沒(méi)有問(wèn)題還是顯然的。

            re: 一個(gè)有趣的小問(wèn)題 OwnWaterloo 2009-09-01 22:42
            @莫失莫忘
            > 如果代碼中越界了,那編譯不會(huì)出錯(cuò)。但是如果運(yùn)行就會(huì)報(bào)錯(cuò)
            對(duì)數(shù)組越界, C++標(biāo)準(zhǔn)中的描述是“未定義”。

            據(jù)說(shuō)有一些平臺(tái)(我只用過(guò)i386和ppc), 確實(shí)會(huì)檢查無(wú)效地址——甚至只是獲取,而沒(méi)有進(jìn)行操作, 例如:
            int a[12];
            int* p = &a[13]; // 就會(huì)引發(fā)錯(cuò)誤。

            而在i386下, 后一句代碼只會(huì)產(chǎn)生一個(gè)無(wú)效(無(wú)效的準(zhǔn)確含義是越界)地址, 不會(huì)引發(fā)錯(cuò)誤 —— 甚至對(duì)無(wú)效地址的訪問(wèn)(讀寫)是否會(huì)引發(fā)錯(cuò)誤, 都要看運(yùn)氣。

            首先, &a[13]肯定不是nullptr。 nullptr檢測(cè)區(qū)就不會(huì)起作用。

            其次, vc可能會(huì)在a[12]附近安插一些guard。
            int read = *p; // 無(wú)法檢測(cè)
            *p = 1212; // 檢測(cè)出寫, 如果guard的恰好是1212,越界寫都判斷不出
            guard的值好像是0xcc。

            三, vc的release配置不會(huì)安插guard。

            四, 還有一種能檢測(cè)到錯(cuò)誤的情況。
            p 指向的地址還沒(méi)有被保留或提交。

            因?yàn)閕386 下windows的棧通常是向下增長(zhǎng),
            p = &a[ /* 這里需要一個(gè)很大的正數(shù) */ ]; 才能越過(guò)棧底, 達(dá)到保護(hù)區(qū)。

            p = &a[ /* 如果這里是負(fù)數(shù) */ ] ; 越過(guò)以提交的棧頂, 達(dá)到保護(hù)區(qū)就容易許多。


            如果p誤指的不僅僅是棧上變量, 那還可以增加一些錯(cuò)誤檢測(cè)的機(jī)會(huì)。
            沒(méi)有被保留與提交的頁(yè), 讀寫都會(huì)錯(cuò)。
            已提交的頁(yè), 還得看頁(yè)的保護(hù)屬性。


            說(shuō)了這么多, 就是想說(shuō)所謂的“運(yùn)行就會(huì)報(bào)錯(cuò)”, 是不靠譜的。
            如你所見(jiàn), 上面已經(jīng)列舉出許多運(yùn)行時(shí)不會(huì)報(bào)錯(cuò)的情形——這種情形還不少。

            所以, 大家寫代碼還是要按照規(guī)范來(lái)……
            為什么這種不合乎規(guī)范的代碼不會(huì)出現(xiàn)錯(cuò)誤, 只能作為茶余飯后的談資……
            re: 一個(gè)有趣的小問(wèn)題 OwnWaterloo 2009-09-01 22:05
            @莫失莫忘
            > 自己去看看博主的解釋吧!

            我又看了一遍。
            只有對(duì)S::sum有語(yǔ)意上以及實(shí)現(xiàn)上的解引用。
            對(duì) sa[100].a(); 有語(yǔ)意上的解引用。
            Chuck 這文章正是想得出 —— s[100].a(); 只存在語(yǔ)意上的解引用, 實(shí)際上沒(méi)有進(jìn)行解引用, 所以才不會(huì)出錯(cuò) —— 的觀點(diǎn)。


            > 代碼中出現(xiàn)無(wú)效地址,那通過(guò)無(wú)效地址去訪問(wèn)有效地址按理說(shuō)也是錯(cuò)的喲~~
            > 如果不解引用就不出錯(cuò)的話,但是博主的解釋中就有解引用了。
            依然不明白你在說(shuō)什么……
            共10頁(yè): First 2 3 4 5 6 7 8 9 10 
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(8)

            隨筆檔案(16)

            鏈接

            搜索

            •  

            積分與排名

            • 積分 - 197920
            • 排名 - 133

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            国产亚洲美女精品久久久久狼| 久久久久久精品免费免费自慰| 国产精品久久久久久五月尺| 欧美一区二区久久精品| 精产国品久久一二三产区区别 | 蜜桃麻豆WWW久久囤产精品| 亚洲综合伊人久久综合| 亚洲人成无码www久久久| 国产精品久久99| 无码人妻久久一区二区三区免费丨 | 久久久久人妻精品一区| 久久国产高清一区二区三区| 亚洲人成精品久久久久| 久久免费香蕉视频| 亚洲综合精品香蕉久久网97| 天天爽天天狠久久久综合麻豆| 国产精品美女久久久久av爽| 日产精品久久久一区二区| 亚洲一区中文字幕久久| 久久久久久狠狠丁香| 国产精品国色综合久久| 国产69精品久久久久观看软件| 久久综合狠狠综合久久激情 | 欧美粉嫩小泬久久久久久久 | 久久一本综合| 亚洲AV伊人久久青青草原| 精品无码人妻久久久久久| 爱做久久久久久| 久久久久亚洲AV无码去区首| 久久久久久国产精品免费免费 | 青青草国产精品久久| 四虎国产精品免费久久5151| 精品久久久久国产免费| 久久99精品久久久久久齐齐| 国产精品99久久99久久久| 国内精品久久久久久野外| 国产免费久久精品丫丫| 污污内射久久一区二区欧美日韩| 亚洲а∨天堂久久精品9966| 久久夜色精品国产噜噜亚洲AV | 欧美一级久久久久久久大片|