• <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>
            隨筆-59  評論-36  文章-0  trackbacks-0

            轉(zhuǎn)自:http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx
            作者:孟巖
            --------------------------------------------------------------------------------------
            這是那篇C++0X的正文。太長,先寫上半部分發(fā)了。

            Function/bind可以是一個很簡單的話題,因?yàn)樗鋵?shí)不過就是一個泛型的函數(shù)指針。但是如果那么來談,就沒意思了,也犯不上寫這篇東西。在我看來,這個事情要講的話,就應(yīng)該講透,講到回調(diào)(callback)、代理(delegate)、信號(signal)和消息傳遞(messaging)的層面,因?yàn)樗_實(shí)是太重要了。這個話題不但與面向?qū)ο蟮暮诵乃枷朊芮邢嚓P(guān),而且是面向?qū)ο髢纱罅髋芍g交鋒的中心。圍繞這個問題的思考和爭論,幾乎把20年來所有主流的編程平臺和編程語言都攪進(jìn)來了。所以,如果詳盡鋪陳,這個話題直接可以寫一本書。

            寫書我當(dāng)然沒那個水平,但這個題目確實(shí)一直想動一動。然而這個主題實(shí)在太大,我實(shí)在沒有精力把它完整的寫下來;這個主題也很深,特別是涉及到并發(fā)環(huán)境有關(guān)的話題,我的理解還非常膚淺,總覺得我認(rèn)識的很多高手都比我更有資格寫這個話題。所以猶豫了很久,要不要現(xiàn)在寫,該怎么寫。最后我覺得,確實(shí)不能把一篇博客文章寫成一本20年面向?qū)ο蠹夹g(shù)史記,所以決定保留大的架構(gòu),但是對其中具體的技術(shù)細(xì)節(jié)點(diǎn)到為止。我不會去詳細(xì)地列舉代碼,分析對象的內(nèi)存布局,畫示意圖,但是會把最重要的結(jié)論和觀點(diǎn)寫下來,說得好聽一點(diǎn)是提綱挈領(lǐng),說的不好聽就是語焉不詳。但無論如何,我想這樣一篇東西,一是談?wù)勎覍@個事情的看法,二是“拋磚引玉”,引來高手的關(guān)注,引出更深刻和完整的敘述。

            下面開始。

            0. 程序設(shè)計(jì)有一個范式(paradigm)問題。所謂范式,就是組織程序的基本思想,而這個基本思想,反映了程序設(shè)計(jì)者對程序的一個基本的哲學(xué)觀,也就是說,他認(rèn)為程序的本質(zhì)是什么,他認(rèn)為一個大的程序是由什么組成的。而這,又跟他對于現(xiàn)實(shí)世界的看法有關(guān)。顯然,這樣的看法不可能有很多種。編程作為一門行業(yè),獨(dú)立存在快60年了,但是所出現(xiàn)的范式不過三種——過程范式、函數(shù)范式、對象范式。其中函數(shù)范式與現(xiàn)實(shí)世界差距比較大,在這里不討論。而過程范式和對象范式可以視為對程序本質(zhì)的兩種根本不同的看法,而且能夠分別在現(xiàn)實(shí)世界中找到相應(yīng)的映射。
            過程范式認(rèn)為,程序是由一個又一個過程經(jīng)過順序、選擇和循環(huán)的結(jié)構(gòu)組合而成。反映在現(xiàn)實(shí)世界,過程范式體現(xiàn)了勞動分工之前“全能人”的工作特點(diǎn)——所有的事情都能干,所有的資源都是我的,只不過得具體的事情得一步步地來做。
            對象范式則反映了勞動分工之后的團(tuán)隊(duì)協(xié)作的工作特點(diǎn)——每個人各有所長,各司其職,有各自的私有資源,工件和信息在人們之間彼此傳遞,最后完成工作。因此,對象范式也就形成了自己對程序的看法——程序是由一組對象組成,這些對象各有所能,通過消息傳遞實(shí)現(xiàn)協(xié)作。

            對象范式與過程范式相比,有三個突出的優(yōu)勢,第一,由于實(shí)現(xiàn)了邏輯上的分工,降低了大規(guī)模程序的開發(fā)難度。第二,靈活性更好——若干對象在一起,可以靈活組合,可以以不同的方式協(xié)作,完成不同的任務(wù),也可以靈活的替換和升級。第三,對象范式更加適應(yīng)圖形化、網(wǎng)絡(luò)化、消息驅(qū)動的現(xiàn)代計(jì)算環(huán)境。

            所以,較之于過程范式,對象范式,或者說“面向?qū)ο?#8221;,確實(shí)是更具優(yōu)勢的編程范式。最近看到一些文章抨擊面向?qū)ο?,說面向?qū)ο笫呛?,我認(rèn)為要具體分析。對面向?qū)ο蟮囊徊糠峙u,是沖著主流的“面向?qū)ο?#8221;語言去的,這確實(shí)是有道理的,我在下面也會談到,而且會罵得更狠。而另一個批評的聲音,主要而來自STL之父Alex Stepanov,他說的當(dāng)然有他的道理,不過要知道該牛人是前蘇聯(lián)莫斯科國立羅蒙諾索夫大學(xué)數(shù)學(xué)系博士,你只要翻翻前蘇聯(lián)的大學(xué)數(shù)學(xué)教材就知道了,能夠在莫大拿到數(shù)學(xué)博士的,根本就是披著人皮的外星高等智慧。而我們編寫地球上的程序,可能還是應(yīng)該以地球人的觀點(diǎn)為主。

            1. 重復(fù)一遍對象范式的兩個基本觀念:
            程序是由對象組成的;
            對象之間互相發(fā)送消息,協(xié)作完成任務(wù);

            請注意,這兩個觀念與后來我們熟知的面向?qū)ο笕?#8220;封裝、繼承、多態(tài)”根本不在一個層面上,倒是與再后來的“組件、接口”神合。

            2. 世界上第一個面向?qū)ο笳Z言是Simula-67,第二個面向?qū)ο笳Z言是Smalltalk-71。Smalltalk受到了Simula-67的啟發(fā),基本出發(fā)點(diǎn)相同,但也有重大的不同。先說相同之處,Simula和Smalltalk都秉承上述對象范式的兩個基本觀念,為了方便對象的構(gòu)造,也都引入了類、繼承等概念。也就是說,類、繼承這些機(jī)制是為了實(shí)現(xiàn)對象范式原則而構(gòu)造出來的第二位的、工具性的機(jī)制,那么為什么后來這些第二位的東西篡了主位,后面我會再來分析。而Simula和Smalltalk最重大的不同,就是Simula用方法調(diào)用的方式向?qū)ο蟀l(fā)送消息,而Smalltalk構(gòu)造了更靈活和更純粹的消息發(fā)送機(jī)制。

            具體的說,向一個Simula對象中發(fā)送消息,就是調(diào)用這個對象的一個方法,或者稱成員函數(shù)。那么你怎么知道能夠在這個對象上調(diào)用這個成員函數(shù)呢?或者說,你怎么知道能夠向這個對象發(fā)送某個消息呢?這就要求你必須確保這個對象具有合適的類型,也就是說,你得先知道哦這個對象是什么,才能向它發(fā)消息。而消息的實(shí)現(xiàn)方式被直接處理為成員函數(shù)調(diào)用,或虛函數(shù)調(diào)用。

            而Smalltalk在這一點(diǎn)上做了一個歷史性的跨越,它實(shí)現(xiàn)了一個與目標(biāo)對象無關(guān)的消息發(fā)送機(jī)制,不管那個對象是誰,也不管它是不是能正確的處理一個消息,作為發(fā)送消息的對象來說,可以毫無顧忌地抓住一個對象就發(fā)消息過去。接到消息的對象,要嘗試?yán)斫膺@個消息,并最后調(diào)用自己的過程來處理消息。如果這個消息能被處理,那個對象自然會處理好,如果不能被處理,Smalltalk系統(tǒng)會向消息的發(fā)送者回傳一個doesNotUnderstand消息,予以通知。對象不用關(guān)心消息是如何傳遞給另一個對象的,傳遞過程被分離出來(而不是像Simula那樣明確地被以成員函數(shù)調(diào)用的方式實(shí)現(xiàn)),可以是在內(nèi)存中復(fù)制,也可以是進(jìn)程間通訊。到了Smalltalk-80時(shí),消息傳遞甚至可以跨越網(wǎng)絡(luò)。

            為了方便后面的討論,不妨把源自Simula的消息機(jī)制稱為“靜態(tài)消息機(jī)制”,把源自Smalltalk的消息機(jī)制稱為“動態(tài)消息機(jī)制”。

            Simula與Smalltalk之間對于消息機(jī)制的不同選擇,主要是因?yàn)閮烧哂谟猛?。前者是用于仿真程序開發(fā),而后者用于圖形界面環(huán)境構(gòu)建,看上去各自合情合理。然而,就是這么一點(diǎn)簡單的區(qū)別,卻造成了巨大的歷史后果。

            3. 到了1980年代,C++出現(xiàn)了。Bjarne Stroustrup在博士期間深入研究過Simula,非常欣賞其思想,于是就在C語言語法的基礎(chǔ)之上,幾乎把Simula的思想照搬過來,形成了最初的C++。C++問世以之初,主要用于解決規(guī)模稍大的傳統(tǒng)類型的編程問題,迅速取得了巨大的成功,也證明了對象范式本身所具有的威力。

            大約在同期,Brad Cox根據(jù)Smalltalk的思想設(shè)計(jì)了Objective-C,可是由于其語法怪異,沒有流行起來。只有Steve Jobs這種具有禪宗美學(xué)鑒賞力的世外高人,把它奉為瑰寶,與1988年連鍋把Objective-C的團(tuán)隊(duì)和產(chǎn)品一口氣買了下來。

            4. 就在同一時(shí)期,GUI成為熱門。雖然GUI的本質(zhì)是對象范型的,但是當(dāng)時(shí)(1980年代中期)的面向?qū)ο笳Z言,包括C++語言,還遠(yuǎn)不成熟,因此最初的GUI系統(tǒng)無一例外是使用C和匯編語言開發(fā)的。或者說,最初的GUI開發(fā)者硬是用抽象級別更低的語言構(gòu)造了一個面向?qū)ο笙到y(tǒng)。熟悉Win32 SDK開發(fā)的人,應(yīng)該知道我在說什么。

            5. 當(dāng)時(shí)很多人以為,如果C++更成熟些,直接用C++來構(gòu)造Windows系統(tǒng)會大大地容易。也有人覺得,盡管Windows系統(tǒng)本身使用C寫的,但是其面向?qū)ο蟮谋举|(zhì)與C++更契合,所以在其基礎(chǔ)上包裝一個C++的GUI framework一定是輕而易舉??墒且粍邮秩藗兙桶l(fā)現(xiàn),完全不是那么回事。用C++開發(fā)Windows框架難得要死。為什么呢?主要就是Windows系統(tǒng)中的消息機(jī)制實(shí)際上是動態(tài)的,與C++的靜態(tài)消息機(jī)制根本配合不到一起去。在Windows里,你可以向任何一個窗口發(fā)送消息,這個窗口自己會在自己的wndproc里來處理這個消息,如果它處理不了,就交給default window/dialog proc去處理。而在C++里,你要向一個窗口發(fā)消息,就得確保這個窗口能處理這個消息,或者說,具有合適的類型。這樣一來的話,就會導(dǎo)致一個錯綜復(fù)雜的窗口類層次結(jié)構(gòu),無法實(shí)現(xiàn)。而如果你要讓所有的窗口類都能處理所有可能的消息,且不論這樣在邏輯上就行不通(用戶定義的消息怎么處理?),單在實(shí)現(xiàn)上就不可接受——為一個小小的不同就得創(chuàng)造一個新的窗口類,每一個小小的窗口類都要背上一個多達(dá)數(shù)百項(xiàng)的v-table,而其中可能99%的項(xiàng)都是浪費(fèi),不要說在當(dāng)時(shí),就是在今天,內(nèi)存數(shù)量非常豐富的時(shí)候,如果每一個GUI程序都這么搞,用戶也吃不消。

            6. 實(shí)際上C++的靜態(tài)消息機(jī)制還引起了更深嚴(yán)重的問題——扭曲了人們對面向?qū)ο蟮睦斫?。既然必須要先知道對象的類型,才能向?qū)ο蟀l(fā)消息,那么“類”這個概念就特別重要了,而對象只不過是類這個模子里造出來的東西,反而不重要。漸漸的,“面向?qū)ο缶幊?#8221;變成了“面向類編程”,“面向類編程”變成了“構(gòu)造類繼承樹”。放在眼前的鮮活的對象活動不重要了,反而是其背后的靜態(tài)類型系統(tǒng)成為關(guān)鍵。“封裝、繼承”這些第二等的特性,喧賓奪主,儼然成了面向?qū)ο蟮囊?。每個程序員似乎都要先成為領(lǐng)域?qū)<?,然后成為領(lǐng)域分類學(xué)專家,然后構(gòu)造一個完整的繼承樹,然后才能new出對象,讓程序跑起來。正是因?yàn)檫@個過程太漫長,太困難,再加上C++本身的復(fù)雜度就很大,所以C++出現(xiàn)這么多年,真正堪稱經(jīng)典的面向?qū)ο箢悗旌涂蚣埽瑤缀跚缚蓴?shù)。很多流行的庫,比如MFC、iostream,都暴露出不少問題。一般程序員總覺得是自己的水平不夠,于是下更大功夫去練劍。殊不知根本上是方向錯了,脫離了對象范式的本質(zhì),企圖用靜態(tài)分類法來對現(xiàn)實(shí)世界建模,去刻畫變化萬千的動態(tài)世界。這么難的事,你水平再高也很難做好。

            可以從一個具體的例子來理解這個道理,比如在一個GUI系統(tǒng)里,一個 Push Button 的設(shè)計(jì)問題。事實(shí)上在一個實(shí)際的程序里,一個 push button 到底“是不是”一個 button,進(jìn)而是不是一個 window/widget,并不重要,本質(zhì)上我根本不關(guān)心它是什么,它從屬于哪一個類,在繼承樹里處于什么位置,只要那里有這么一個東西,我可以點(diǎn)它,點(diǎn)完了可以發(fā)生相應(yīng)的效果,就可以了。可是Simula –> C++ 所鼓勵的面向?qū)ο笤O(shè)計(jì)風(fēng)格,非要上來就想清楚,a Push Button is-a Button, a Button is-a Command-Target Control, a Command-Target Control is-a Control, a Control is-a Window. 把這一圈都想透徹之后,才能 new 一個 Push Button,然后才能讓它工作。這就形而上學(xué)了,這就脫離實(shí)際了。所以很難做好。你看到 MFC 的類繼承樹,覺得設(shè)計(jì)者太牛了,能把這些層次概念都想清楚,自己的水平還不夠,還得修煉。實(shí)際上呢,這個設(shè)計(jì)是經(jīng)過數(shù)不清的失敗和錢磨出來、砸出來的,MFC的前身 Afx 不是就失敗了嗎?1995年還有一個叫做 Taligent 的大項(xiàng)目,召集了包括 Eric Gamma 在內(nèi)的一大堆牛人,要用C++做一個一統(tǒng)天下的application framework,最后也以慘敗告終,連公司都倒閉了,CEO車禍身亡,牛人們悉數(shù)遣散。附帶說一下,這個Taligent項(xiàng)目是為了跟NextSTEP和Microsoft Cairo競爭,前者用Objective-C編寫,后來發(fā)展為Cocoa,后者用傳統(tǒng)的Win32 + COM作為基礎(chǔ)架構(gòu),后來發(fā)展為Windows NT。而Objective-C和COM,恰恰就在動態(tài)消息分派方面,與C++迥然不同。后面還會談到。

            客觀地說,“面向類的設(shè)計(jì)”并不是沒有意義。來源于實(shí)踐又高于實(shí)踐的抽象和概念,往往能更有力地把握住現(xiàn)實(shí)世界的本質(zhì),比如MVC架構(gòu),就是這樣的有力的抽象。但是這種抽象,應(yīng)該是來源于長期最佳實(shí)踐的總結(jié)和提高,而不是面對問題時(shí)主要的解決思路。過于強(qiáng)調(diào)這種抽象,無異于假定程序員各個都是哲學(xué)家,具有對現(xiàn)實(shí)世界準(zhǔn)確而深刻的抽象能力,當(dāng)然是不符合實(shí)際情況的。結(jié)果呢,剛學(xué)習(xí)面向?qū)ο鬀]幾天的程序員,對眼前鮮活的對象世界視而不見,一個個都煞有介事地去搞哲學(xué)冥想,企圖越過現(xiàn)實(shí)世界,去抽象出其背后本質(zhì),當(dāng)然敗得很慘。

            其實(shí)C++問世之后不久,這個問題就暴露出來了。第一個C++編譯器 Cfront 1.0 是單繼承,而到了 Cfront 2.0,加入了多繼承。為什么?就是因?yàn)槭褂弥腥藗儼l(fā)現(xiàn)邏輯上似乎完美的靜態(tài)單繼承關(guān)系,碰到復(fù)雜靈活的現(xiàn)實(shí)世界,就破綻百出——蝙蝠是鳥也是獸,水上飛機(jī)能飛也能游,它們該如何歸類呢?本來這應(yīng)該促使大家反思繼承這個機(jī)制本身,但是那個時(shí)候全世界陷入繼承狂熱,于是就開始給繼承打補(bǔ)丁,加入多繼承,進(jìn)而加入虛繼承,。到了虛繼承,明眼人一看便知,這只是一個語法補(bǔ)丁,是為了逃避職責(zé)而制造的一塊無用的遮羞布,它已經(jīng)完全已經(jīng)脫離實(shí)踐了——有誰在事前能夠判斷是否應(yīng)該對基類進(jìn)行虛繼承呢?

            到了1990年代中期,問題已經(jīng)十分明顯。UML中有一個對象活動圖,其描述的就是運(yùn)行時(shí)對象之間相互傳遞消息的模型。1994年Robert C. Martin在《Object-Oriented C++ Design Using Booch Method》中,曾建議面向?qū)ο笤O(shè)計(jì)從對象活動圖入手,而不是從類圖入手。而1995年出版的經(jīng)典作品《Design Patterns》中,建議優(yōu)先考慮組合而不是繼承,這也是盡人皆知的事情。這些跡象表明,在那個時(shí)候,面向?qū)ο笊鐓^(qū)里的思想領(lǐng)袖們,已經(jīng)意識到“面向類的設(shè)計(jì)”并不好用。只可惜他們的革命精神還不夠。

            7. 你可能要問,Java 和.NET也是用繼承關(guān)系組織類庫,并進(jìn)行設(shè)計(jì)的啊,怎么那么成功呢?這里有三點(diǎn)應(yīng)該注意。第一,C++的難不僅僅在于其靜態(tài)結(jié)構(gòu)體系,還有很多源于語言設(shè)計(jì)上的包袱,比如對C的兼容,比如沒有垃圾收集機(jī)制,比如對效率的強(qiáng)調(diào),等等。一旦把這些包袱丟掉,設(shè)計(jì)的難度確實(shí)可以大大下降。第二,Java和.NET的核心類庫是在C++十幾年成功和失敗的經(jīng)驗(yàn)教訓(xùn)基礎(chǔ)之上,結(jié)合COM體系優(yōu)點(diǎn)設(shè)計(jì)實(shí)現(xiàn)的,自然要好上一大塊。事實(shí)上,在Java和.NET核心類庫的設(shè)計(jì)中很多地方,體現(xiàn)的是基于接口的設(shè)計(jì),和真正的基于對象的設(shè)計(jì)。有了這兩個主角站臺,“面向類的設(shè)計(jì)”不能喧賓奪主,也能發(fā)揮一些好的作用。第三,如后文指出,Java和.NET中分別對C++最大的問題——缺少對象級別的delegate機(jī)制做出了自己的回應(yīng),這就大大彌補(bǔ)了原來的問題。

            盡管如此,Java還是沾染上了“面向類設(shè)計(jì)”的癌癥,基礎(chǔ)類庫里就有很多架床疊屋的設(shè)計(jì),而J2EE/Java EE當(dāng)中,這種形而上學(xué)的設(shè)計(jì)也很普遍,所以也引發(fā)了好幾次輕量化的運(yùn)動。這方面我并不是太懂,可能需要真正的Java高手出來現(xiàn)身說法。我對Java的看法以前就講過——平臺和語言核心非常好,但風(fēng)氣不好,崇尚華麗繁復(fù)的設(shè)計(jì),裝牛逼的人太多。

            至于.NET,我聽陳榕介紹過,在設(shè)計(jì).NET的時(shí)候,微軟內(nèi)部對于是否允許繼承爆發(fā)了非常激烈的爭論。很多資深高人都強(qiáng)烈反對繼承。至于最后引入繼承,很大程度上是營銷需要壓倒了技術(shù)理性。盡管如此,由于有COM的基礎(chǔ),又實(shí)現(xiàn)了非常徹底的delegate,所以 .NET 的設(shè)計(jì)水平還是很高的。它的主要問題不在這,在于太急于求勝,更新速度太快,基礎(chǔ)不牢。當(dāng)然,根本問題還是微軟沒有能夠在Web和Mobile領(lǐng)域里占到多大的優(yōu)勢,也就使得.NET沒有用武之地。

            8. COM。COM的要義是,軟件是由COM Components組成,components之間彼此通過接口相互通訊。這是否讓你回想起本文開篇所提出的對象范型的兩個基本原則?有趣的是,在COM的術(shù)語里,“COM Component ” 與“object ”通假,這就使COM的心思昭然若揭了。Don Box在Essential COM里開篇就說,COM是更好的C++,事實(shí)上就是告訴大家,形而上學(xué)的“面向類設(shè)計(jì)”不好使,還是回到對象吧。

            用COM開發(fā)的時(shí)候,一個組件“是什么”不重要,它具有什么接口,也就是說,能夠?qū)λl(fā)什么消息,才是重要的。你可以用IUnknown::QueryInterface問組件能對哪一組消息作出反應(yīng)。向組件分派消息也不一定要被綁定在方法調(diào)用上,如果實(shí)現(xiàn)了 IDispatch,還可以實(shí)現(xiàn)“自動化”調(diào)用,也就是COM術(shù)語里的 Automation,而通過 列集(mashal),可以跨進(jìn)程、跨網(wǎng)絡(luò)向另一組件發(fā)送消息,通過 moniker,可以在分布式系統(tǒng)里定位和發(fā)現(xiàn)組件。如果你抱著“對象——消息”的觀念去看COM的設(shè)計(jì),就會意識到,整個COM體系就是用規(guī)范如何做對象,如何發(fā)消息的?;蛘吒卑滓稽c(diǎn),COM就是用C/C++硬是模擬出一個Smalltalk。而且COM的概念世界里沒有繼承,就其純潔性而言,比Smalltalk還Smalltalk。在對象泛型上,COM達(dá)到了一個高峰,領(lǐng)先于那個時(shí)代,甚至于比它的繼任.NET還要純潔。

            COM的主要問題是它的學(xué)習(xí)難度和安全問題,而且,它過于追求純潔性,完全放棄了“面向類設(shè)計(jì)” 的機(jī)制,顯得有點(diǎn)過。

            9. 好像有點(diǎn)扯遠(yuǎn)了,其實(shí)還是在說正事。上面說到由于C++的靜態(tài)消息機(jī)制,導(dǎo)致了形而上學(xué)的“面向類的設(shè)計(jì)”,禍害無窮。但實(shí)際上,C++是有一個補(bǔ)救機(jī)會的,那就是實(shí)現(xiàn)對象級別的delegate機(jī)制。學(xué)過.NET的人,一聽delegate這個詞就知道是什么意思,但Java里沒有對應(yīng)機(jī)制。在C++的術(shù)語體系里,所謂對象級別delegate,就是一個對象回調(diào)機(jī)制。通過delegate,一個對象A可以把一個特定工作,比如處理用戶的鼠標(biāo)事件,委托給另一個對象B的一個方法來完成。A不必知道B的名字,也不用知道它的類型,甚至都不需要知道B的存在,只要求B對象具有一個簽名正確的方法,就可以通過delegate把工作交給B的這個方法來執(zhí)行。在C語言里,這個機(jī)制是通過函數(shù)指針實(shí)現(xiàn)的,所以很自然的,在C++里,我們希望通過指向成員函數(shù)的指針來解決類似問題。

            然而就在這個問題上,C++讓人扼腕痛惜。

            posted on 2011-01-20 22:24 zhaoyg 閱讀(471) 評論(0)  編輯 收藏 引用 所屬分類: C/C++學(xué)習(xí)筆記
            久久中文精品无码中文字幕| 亚洲精品无码专区久久同性男| 亚洲精品乱码久久久久久蜜桃| 午夜精品久久久久久影视riav| 久久久久亚洲AV片无码下载蜜桃| 色综合久久久久综合体桃花网| 9久久9久久精品| 亚洲国产高清精品线久久| 无码八A片人妻少妇久久| 久久国产色AV免费看| 久久天天躁狠狠躁夜夜不卡| 日韩精品久久久久久免费| 精品久久久久久国产三级| 午夜精品久久久久久毛片| 久久精品成人| 精品999久久久久久中文字幕| 久久午夜免费视频| 国产激情久久久久影院小草| 亚洲AV日韩精品久久久久| 久久国产三级无码一区二区| 精品国产乱码久久久久久郑州公司| 久久国产成人午夜aⅴ影院| 99精品久久久久中文字幕| 人妻无码中文久久久久专区| 性做久久久久久久久| 精品无码久久久久久久动漫| 99国产欧美精品久久久蜜芽| 亚洲午夜久久久久久久久电影网 | 久久热这里只有精品在线观看| 午夜不卡888久久| 久久国产精品-国产精品| 亚洲精品乱码久久久久66| 亚洲国产成人久久一区WWW| 久久性精品| 尹人香蕉久久99天天拍| 色婷婷噜噜久久国产精品12p| 51久久夜色精品国产| 国产激情久久久久影院老熟女免费 | 伊人久久综合成人网| 中文精品99久久国产 | 国产高潮久久免费观看|