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

             

            stl的抽象缺陷終結(jié)

            古龍說過,一個(gè)人的最大優(yōu)點(diǎn)往往將是其致命的弱點(diǎn)。這句話用在stl的迭代器上,最是合適不過。stl通過迭代器來解耦容器與算法,可謂擊節(jié)贊嘆;但是,讓迭代器滿世界的到處亂跑,未免就大煞風(fēng)景。此話怎講?

            其實(shí),有些語言就沒有迭代器的概念,并且還活得很優(yōu)雅,好比haskelllist啊、tree啊,壓根就不需要什么迭代器,只需要模式匹配,體現(xiàn)其數(shù)據(jù)結(jié)構(gòu)的遞歸特點(diǎn),就可以很優(yōu)雅地表達(dá)算法。就是javac#C++這幾個(gè)破面向?qū)ο笳Z言,才需要大用特用迭代器,沒有迭代器就活不下去了。迭代器的出現(xiàn)就是為了彌補(bǔ)其語言喪失清晰表達(dá)遞歸數(shù)據(jù)結(jié)構(gòu)的能力。看到haskelllistc++stl下的對(duì)應(yīng)樣子,很多人都表示很難過,因?yàn)?/span>stl里面,list根本就沒有tail函數(shù),更逞論支持listtail還是一個(gè)list這樣絕妙的idea。一切必須通過迭代器這個(gè)萬金油來糊弄其尷尬的困境。

            隨便來看看幾行stl算法函數(shù)的代碼

            Vector<int> nums = {..};
            find(nums.begin(), nums.end(), 
            2);
            remove_if(nums.begin(), nums.end(), _1 
            >= 0); //為了省事,用了bll的風(fēng)格,在c++11中,要從零開始造一個(gè)bll風(fēng)格的輪子,不能更方便,大概也就兩三百行的代碼

            看到?jīng)]有,你信不信,隨便統(tǒng)計(jì)一下,一打的algorithm函數(shù),起碼就有12個(gè)函數(shù)的調(diào)用之道,必須傳遞container.begin()container.end()beginend這對(duì)兄弟,總是成雙成對(duì)的出現(xiàn),說明了一件事情,就是從一開始,它們必須被打包在一起,而不應(yīng)該硬生生地將它們拆開。知道這一拆開,帶來多少問題嗎?代碼上的累贅還算是小事,比如,簡(jiǎn)潔清晰流暢的find(nums, 2),卻要生硬的寫成find(nums.begin(), nums.end(), 2)。當(dāng)然,這種api設(shè)計(jì),也并非一無是處,起碼,在表達(dá)容器里面的部分區(qū)間時(shí),很方便,好比下面的代碼

            int nums[10] = {…};

            find(nums+1, end(nums)-1, 2);

            看起來,好像的確挺方便的,將beginend放在一起,要表達(dá)這樣的概念,似乎就有些麻煩,但其實(shí),這是假象,當(dāng)角度變換時(shí),我們可以會(huì)有更方便的方式來表達(dá)這樣的需求。最起碼,容器的部分區(qū)間也應(yīng)該是由容器本身來表達(dá),而不應(yīng)轉(zhuǎn)嫁給迭代器來應(yīng)付,數(shù)組的部分也是數(shù)組,樹的分支也是樹,這樣的概念,就應(yīng)該由容器本身來定義。像是哈希表就不支持部分區(qū)間的概念。

            為何algorithm的算法,全部(不是基本)都要求一對(duì)迭代器。那是因?yàn)檫@些算法的輸入對(duì)象,本來就是一個(gè)數(shù)據(jù)集合。而一個(gè)迭代器無法完整地表達(dá)一個(gè)容器,起碼必須一對(duì)迭代器才能完整地表達(dá)一個(gè)數(shù)據(jù)集。但是,用一對(duì)迭代器來作為入?yún)ⅲ陀靡粋€(gè)區(qū)間作為入?yún)ⅲw現(xiàn)抽象的側(cè)重點(diǎn)完全不同,而由于此種不同,最后的演變結(jié)果,也是天淵之別,即是一對(duì)迭代器設(shè)計(jì)思路是淵,自然,而區(qū)間的設(shè)計(jì)方案,顯然是天。

            再次回顧上文的結(jié)尾,findfind_ifremove, remove_copy, remove_copy_if, remove_if,……有沒有感受,一股濃濃的過程式風(fēng)格,十分的笨重,明顯的非正交,濃烈的c語言風(fēng)格。對(duì)于這樣的api,讓本座對(duì)委員會(huì)的那幫老不死,徹底的絕望了。他們(它們)的審美觀,停留在很低很低的層次上。

            beginend拆分開來的最大問題,其實(shí)也就只是,前一個(gè)函數(shù)的處理結(jié)果,不能平滑的傳遞到下一個(gè)函數(shù)里面去。比如說,現(xiàn)在函數(shù)make_nums返回vector<int>,試比較一下,高下立判。

            auto nums = make_nums();
            find(nums.begin(), nums.end(), 
            2); //一對(duì)迭代器作為入?yún)?/span>
            find(make_nums(), 2);//直接數(shù)據(jù)區(qū)間作為入?yún)?/span>

            說了這么多,我們強(qiáng)烈要求的僅僅是函數(shù)風(fēng)格的api,正交式的函數(shù)設(shè)計(jì),前一個(gè)函數(shù)的處理結(jié)果可以平滑地傳遞給下一個(gè)函數(shù)。總結(jié)algorithm的一坨函數(shù),本質(zhì)上只需filterfoldmapinsert(copy)這屈指可數(shù)的幾個(gè)函數(shù)就可以自由地組合出來,并且還能組合出來algorithm上沒有的效果。首先,這幾個(gè)函數(shù)的返回結(jié)果都是數(shù)據(jù)區(qū)的數(shù)據(jù)對(duì)象(里面有beginend的成員函數(shù),用以返回迭代器)。其次,就是在迭代器上面做文章,以支持filtermap等操作,也就是在*++!=這幾個(gè)運(yùn)算符上做花樣,要達(dá)到filtermap的效果,很容易的。至于像是要求隨機(jī)訪問迭代器概念的函數(shù),太常用的就做到array_view里面好了,或者就明確規(guī)定入?yún)⒕褪?/span>array_view

            然后stl里面還臆造了一種好像叫做insert_iterator迭代器類型的適配器,用以通過迭代器的語法往容器里頭插入數(shù)據(jù),好像很玄妙,實(shí)則就是強(qiáng)行拔高迭代器的用途,完全就違背了迭代器出現(xiàn)的初衷。這種扭曲的想法,完全就是上面那一坨病態(tài)api的產(chǎn)物。所以,原本的api設(shè)計(jì),算法函數(shù)必須以容器(數(shù)據(jù)區(qū)間)為入?yún)ⅲ瑑?nèi)部調(diào)用其beginend成員函數(shù)獲得迭代器來遍歷容器的函數(shù),何其清晰的設(shè)計(jì)思路。但是,stl的設(shè)計(jì)思路,導(dǎo)致迭代器泛濫,甚至連客戶層面的代碼也大把大把的迭代器,于是迭代器的問題就接二連三的產(chǎn)生,什么失效啊,什么firstlast匹對(duì)錯(cuò)誤。還有,導(dǎo)致容器里面的關(guān)于迭代器的成員函數(shù)多了一倍,哈希表里面也沒有類似于C#DictionaryKeysValues屬性函數(shù),這些用起來很方便的,不是嗎?

            stl的這種api設(shè)計(jì)思路完全不是以方便使用為主,而是以滿足自己的獨(dú)特口味為目的。看看find函數(shù),它返回一個(gè)迭代器,所以,我們使用時(shí),必須通過用end來判斷要找的東西是否在區(qū)間里面,

            auto found = find(nums.begin(), nums.end(), 2);

            if (found != nums.end()){…}

            依本座看,直接就返回指針好了,指針為nullptr,就表示元素找不到,代碼變成這樣

            if (auto found = find(nums, 2)){…}

            代碼合并成一行,不用再和end比較了。更重要的是,返回結(jié)果就是指針,類型非常明確,可以平滑的傳遞到別的函數(shù)里;而不是迭代器類型,誰知道迭代器類型是什么類型。template這種東西的類型,能明確下來時(shí),就盡快明確下來。至于說,有些區(qū)間的元素不支持返回地址,好比,vector<bool>,很簡(jiǎn)單,那就不支持好了。本座編寫c++代碼的原則之一,不求大而全,需求專一,絕不會(huì)因?yàn)閭€(gè)別同學(xué),就犧牲大多數(shù)情況下清晰方便高效的api風(fēng)格。對(duì)于這些異數(shù),必要時(shí),用奇技淫巧解決。你知道,因?yàn)槎嗬^承,虛繼承,把成員函數(shù)指針這個(gè)簡(jiǎn)潔的概念搞得非常復(fù)雜,不能按正常人方式來使用了,嚴(yán)重影響成員函數(shù)的用范圍,一直讓本座耿耿于懷。其實(shí),95%以上的情況下,我們就僅僅需要普通成員函數(shù)指針而已,另外的5%,也都可以用普通成員函數(shù)來封裝。所以,為了彌補(bǔ)這個(gè)遺憾,本座做了一個(gè)精簡(jiǎn)版的delegate,只接受全局函數(shù)和普通成員函數(shù),當(dāng)字段object為空,就表示字段函數(shù)指針是全局函數(shù),不為空,就表示函數(shù)指針是成員函數(shù)。至于其他一切奇奇怪怪的函數(shù),本座的這個(gè)delegatesay no,明確拒絕。

            stl的這種獨(dú)特到處都是,boost更是將其發(fā)揚(yáng)光大,反正設(shè)計(jì)出來的api,就是不考慮讓你用的舒爽,二進(jìn)制的布局,更加一塌糊涂。比如,any的使用,是這樣子用的,cout << any_cast<int>(anyValue),這里還好,假如要分別針對(duì)any的實(shí)際類型來寫代碼,必須這樣子:
            if(anyValue.type() == typeid(int))
                cout 
            << any_cast<int>(anyValue);
            else if (anyValue.type() == typeid(double))
                cout 
            << any_cast< double >(anyValue);

            這種對(duì)類型安全無理取鬧的強(qiáng)調(diào),讓人火冒三丈。要本座說,直接在any里面添加Cast模板成員函數(shù),結(jié)果就返回指針好了,指針為空,就表示類型不匹配,代碼就變成這樣

            if(auto value = anyValue.Cast<int>())
                cout 
            << *value;
            else if(auto value = anyValue.Cast< double >())
                cout 
            << *value;

            是否就沒那么心煩呢。另外,鑒于stl對(duì)于反射的拒絕,采用virtual+template的類型拭擦大法來彌補(bǔ),其實(shí)并不怎么完美。本座用反射重新實(shí)現(xiàn)的any,比stlany好多了,不管是性能、編譯速度、使用方便上,都是要好太多。還有,stlany,要為每個(gè)用到的類型都要生成一個(gè)實(shí)實(shí)在在的多態(tài)具體類,每個(gè)類都要有一個(gè)專門的虛函數(shù)表對(duì)應(yīng),這些可都要寫到二進(jìn)制文件里面,代碼就是這樣膨脹起來的。總之,stl回避反射后,反射就以另一種形式回歸,好比virtual+template,好比%d%s,好比locale的那些facet實(shí)現(xiàn), 這些動(dòng)態(tài)機(jī)制各自為政,各種混亂。還不如干脆就從源頭上系統(tǒng)化公理化地給予終極解決。

            所以,總體上感受stl設(shè)計(jì)思路上存在的路線,就是太在意于c++語言本身上的特點(diǎn),受語言自身的缺陷復(fù)雜影響太多,忽略了真正的需求,太多的臆造需求,強(qiáng)行讓需求來遷就語言,而不是讓語言來配合基礎(chǔ)庫的實(shí)際普遍需求,需求才是根本,為了可以最方便,最清晰,最性能的基礎(chǔ)庫,完全可以大規(guī)模地使用宏、挖掘語言里面最黑暗的邊角料,甚至為了庫的清晰性,可以拒絕那些用了復(fù)雜特性的數(shù)據(jù)結(jié)構(gòu),比如多繼承,虛繼承等無聊玩意。

            概括起來,路線問題導(dǎo)致最終的正果,也即是stl的具體弱雞表現(xiàn)就是,最根本是二進(jìn)制接口使用上的重重阻礙,誰敢在動(dòng)態(tài)庫api使用stl的數(shù)據(jù)類型。其次是以下5小點(diǎn):

            1、內(nèi)存分配器不應(yīng)該是容器的模板參數(shù),對(duì)allocator的處理太過草率,當(dāng)初這里必須做深入的挖掘,c++完全可以實(shí)現(xiàn)一定程度上的垃圾回收功能,比如arean allocator,不必一一回收在arena allocator上分配的對(duì)象,只需一次性釋放arena allocator的內(nèi)存,達(dá)到多次分配,一次釋放的高性能效果,還避免內(nèi)存泄露,也不用直接面對(duì)循環(huán)引用的怪胎設(shè)計(jì)問題。現(xiàn)有的內(nèi)存管理策略,把壓力都放在智能指針上;

            2、提供的通用容器不夠完備;原本stl的數(shù)據(jù)結(jié)構(gòu)就大可滿足所有正常和非正常的使用場(chǎng)合,比如滿足侵入式的鏈表需求,比如不管理元素生命周期的容器等;

            3、過多的暴露迭代器,迭代器的應(yīng)用范圍過廣,stl的算法函數(shù)用起來很不方便;

            4、回避動(dòng)態(tài)類型反射信息,對(duì)數(shù)據(jù)的輸入輸出支持非常單薄,包括字符串處理、文件讀寫、網(wǎng)絡(luò)數(shù)據(jù)收發(fā)等,標(biāo)準(zhǔn)庫上的現(xiàn)有那點(diǎn)小功能,僅僅是聊勝于無而已,難堪大任;

            5、非容器系的實(shí)用類太少;

            一句話,目前stl的使用,還是遠(yuǎn)遠(yuǎn)不夠爽。原本用上stl的代碼,應(yīng)該可以更短、更快、更小。只可惜,stl在通過迭代器實(shí)現(xiàn)算法與容器的分離之后,就停步不前,其設(shè)計(jì)體系在別的地方,鮮有建樹創(chuàng)新。戰(zhàn)略高度過于局促,很多復(fù)雜難搞的問題,其實(shí)都蘊(yùn)含著絕大的機(jī)遇,而stl都一一回避,真是回避得好!


            posted on 2017-07-10 18:30 華夏之火 閱讀(958) 評(píng)論(0)  編輯 收藏 引用 所屬分類: c++技術(shù)探討

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            搜索

            積分與排名

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久精品人妻一区二区三区蜜桃 | 国产成人精品综合久久久久| 一级做a爰片久久毛片免费陪| 一本大道久久东京热无码AV| 久久99精品久久久久子伦| 大蕉久久伊人中文字幕| 精品国产青草久久久久福利| 久久免费国产精品一区二区| 66精品综合久久久久久久| 亚洲第一永久AV网站久久精品男人的天堂AV| 久久亚洲精品无码aⅴ大香| 成人久久久观看免费毛片| 欧美精品丝袜久久久中文字幕| 国产激情久久久久影院老熟女| 中文字幕无码av激情不卡久久| 国内精品久久久久| 久久久久亚洲AV成人网人人网站| 国产精品久久久99| 粉嫩小泬无遮挡久久久久久| 亚洲国产香蕉人人爽成AV片久久 | 久久精品这里热有精品| 久久天天躁狠狠躁夜夜2020一| 久久久久久久综合综合狠狠| 国产激情久久久久影院小草 | 热re99久久精品国99热| 无码任你躁久久久久久老妇App| AA级片免费看视频久久| 久久精品国内一区二区三区| 国产精品久久久久aaaa| 久久大香香蕉国产| MM131亚洲国产美女久久| 欧美黑人又粗又大久久久| 亚洲AV日韩精品久久久久| 久久亚洲熟女cc98cm| 亚洲国产精品无码成人片久久| 久久无码国产专区精品| 97精品依人久久久大香线蕉97| 伊人久久精品无码二区麻豆| 亚洲乱码精品久久久久..| 久久久久亚洲AV片无码下载蜜桃| 国产综合久久久久久鬼色|