青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

longshanks

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

常用鏈接

留言簿(10)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

    本文來(lái)源于TopLanguage Group 上的一次討論(這里 ,這里這里 )。pongba提出:C++的抽象機(jī)制并不完善,原因是為了性能而做的折中,未來(lái)隨著計(jì)算能力的提高到一定程度,人們就能夠忽略更好的抽象所帶來(lái)的負(fù)面效應(yīng)。就此諸老大各自提出高見(jiàn),受益良多啊。經(jīng)過(guò)討論,我基本上理解了pongba的想法。但我覺(jué)得等待計(jì)算機(jī)的性能提高太消極了。我相信隨著編程技術(shù)的發(fā)展,這種最優(yōu)抽象造成的性能損失將會(huì)越來(lái)越小。這種途徑將會(huì)更快地讓人們接受最優(yōu)抽象形式。

     在“C++ Template”一書(shū)中,將多態(tài)總結(jié)為三種主要類(lèi)型:runtime bound、static unbound和runtime unbound。其中runtime bound就是我們通常所說(shuō)的動(dòng)多態(tài),OOP的核心支柱(廣義上OOP還包括Object Base(OB,僅指類(lèi)型封裝等OO的基本特性),但有時(shí)也會(huì)將OB和OOP分開(kāi),OOP單指以O(shè)O為基礎(chǔ)的動(dòng)多態(tài)。這里使用狹義的OOP含義); static unbound就是靜多態(tài),通過(guò)模板實(shí)現(xiàn)。而runtime unbound則是一種不常見(jiàn)的形式。早年的SmallTalk具有這種形式,現(xiàn)在的ruby也引入這種機(jī)制。
     在主流的(靜態(tài))語(yǔ)言中,我們會(huì)面臨兩種類(lèi)型的多態(tài)需求:對(duì)于編譯期可以確定類(lèi)型的,使用靜多態(tài),比如實(shí)例化一個(gè)容器;對(duì)于運(yùn)行期方能確定類(lèi)型的,則使用 動(dòng)多態(tài)。而runtime unbound也可以用于運(yùn)行期類(lèi)型決斷。于是,便有了兩種運(yùn)行期多態(tài)。這兩種多態(tài)的特性和他們的差異,是本文的核心。實(shí)際上,相比動(dòng)多態(tài), runtime unbound多態(tài)為我們提供了更本質(zhì)的運(yùn)行時(shí)多態(tài)手段,我們可以從中獲得更大的收益。但是鑒于一些技術(shù)上的困難,runtime unbound多態(tài)無(wú)法進(jìn)入主流世界。不過(guò),由于新的編程技術(shù)的出現(xiàn),使得這種更好的運(yùn)行時(shí)多態(tài)形式可以同動(dòng)多態(tài)一比高下。

動(dòng)多態(tài)   

    廢話(huà)少說(shuō),讓我們從一個(gè)老掉牙的案例開(kāi)始吧:編寫(xiě)一個(gè)繪圖程序,圖形包括矩形、橢圓、三角形、多邊形等等。圖形從腳本(比如xml)中讀出,創(chuàng)建后保存在一個(gè)容器中備查。通過(guò)遍歷容器執(zhí)行圖形繪制。
    就這么個(gè)題目,很簡(jiǎn)單,也很熟悉,解釋OOP的動(dòng)多態(tài)最常用的案例。下面我們就從動(dòng)多態(tài)實(shí)現(xiàn)開(kāi)始。
    首先定義一個(gè)抽象基類(lèi),也就是接口:

    class IShape

    {

        virtual void load(xml init)=0;

        virtual void draw(monitor m)=0;

        ...

    };

    然后定義各種圖形類(lèi),并從這個(gè)接口上繼承:

    class Rectangle: public IShape

    {

        void load(xml init) {...}

        void draw(monitor m) {...}

        ...

    };

    class Ellipse: public IShape

    {

        void load(xml init) {...}

        void draw(monitor m) {...}

        ...

    };

    ...

 

    void DrawShapes(monitor m, vector<IShape*> const& g)

    {

        vector<IShape*>::const_iterator b(g.begin()), e(g.end());

        for(; b!=e; ++b)

        {

            (*b)->draw(m);

        }

    }

    ...

    現(xiàn)在可以使用這些圖形類(lèi)了:

    vector<IShape*> vg;

    vg.push_back(new Rectangle);

    vg.push_back(new Ellipse);

    ...

    DrawShapes(crt, vg);

    通過(guò)接口IShape,我們可以把不同的圖形類(lèi)統(tǒng)一到一種類(lèi)型下。但是,通過(guò)虛函數(shù)的override,由圖形類(lèi)實(shí)現(xiàn)IShape上的虛函數(shù)。這可以算老 生常談了。動(dòng)多態(tài)的核心就是利用override和late bound的組合,使得一個(gè)基類(lèi)可以在類(lèi)型歸一化的情況下,擁有繼承類(lèi)的語(yǔ)義。OOP設(shè)計(jì)模式大量運(yùn)用這種技術(shù),實(shí)現(xiàn)很多需要靈活擴(kuò)展的系統(tǒng)。

Runtime Unbound

    Runtime Unbound多態(tài)混合了靜多態(tài)和動(dòng)多態(tài)的特征,即既有類(lèi)型泛化,又是運(yùn)行時(shí)決斷的。一個(gè)最典型的例子就是ruby的函數(shù):
    class x
       def fun(car)
            car.aboard
        end
    end
    這個(gè)案例非常明確地展示出了Runtime Unbound多態(tài)的特點(diǎn)。car參數(shù)沒(méi)有類(lèi)型,這里也不需要關(guān)心類(lèi)型,只要求car對(duì)象有一個(gè)aboard方法即可。由于ruby是動(dòng)態(tài)語(yǔ)言,能夠運(yùn)行時(shí)檢測(cè)對(duì)象的特征,并動(dòng)態(tài)調(diào)用對(duì)象上的方法。
    在Runtime Unbound的思想指導(dǎo)下,我們利用一種偽造的“動(dòng)態(tài)C++”,把上面的繪圖例子重新編寫(xiě):

    class Rectangle

    {

        void load(xml init) {...}

        void draw(monitor dev) {...}

        ...

    };

    class Ellipse

    {

        void load(xml init) {...}

        void draw(monitor dev) {...}

        ...

    };

    ...

    void DrawShapes(monitor dev, vector<anything> const& g)

    {

        vector<IShape>::const_iterator b(g.begin()), e(g.end());

        for(; b!=e; ++b)

        {

            (*b).draw(dev);

        }

    }

    ...

    vector<anything> vg;

    vg.push_back(Rectangle(...));

    vg.push_back(Ellipse(...));

    ...

    DrawShapes(crt, vg);

    圖形類(lèi)不再?gòu)某橄蠼涌贗Shape繼承,而用關(guān)鍵字anything實(shí)例化vector<>模板。這個(gè)虛構(gòu)的anything關(guān)鍵字所起的作 用就是使得vector能夠接受不同類(lèi)型的對(duì)象。當(dāng)DrawShapes()函數(shù)接收到存放圖形對(duì)象的容器后,遍歷每一個(gè)對(duì)象,并且調(diào)用對(duì)象上的draw ()函數(shù),而不管其類(lèi)型。
    從這段代碼中,我們可以看出Runtime Unbound多態(tài)帶來(lái)的好處。所有圖形類(lèi)不再需要?dú)w一化成一個(gè)類(lèi)型(抽象接口)。每個(gè)類(lèi)只需按照約定,實(shí)現(xiàn)load、draw等成員函數(shù)即可。也就是 說(shuō),這些圖形類(lèi)解耦合了。一旦類(lèi)型解耦,便賦予我們很大的自由度。最典型的情況就是,我們需要使用一個(gè)其他人開(kāi)發(fā)的圖形類(lèi),并且無(wú)法修改其實(shí)現(xiàn)。此時(shí),如 果使用動(dòng)多態(tài),就很麻煩。因?yàn)楸M管這些圖形類(lèi)都擁有l(wèi)oad、draw等函數(shù),但畢竟不是繼承自IShape,無(wú)法直接插入容器。必須編寫(xiě)一個(gè)繼承自 IShape的適配器,作為外來(lái)圖形類(lèi)的包裝,轉(zhuǎn)發(fā)對(duì)其的訪問(wèn)。表面上,我們只是減少一個(gè)接口的定義,但Runtime Unbound多態(tài)帶來(lái)的解耦有著非凡的意義。因?yàn)轭?lèi)耦合始終是OOP設(shè)計(jì)中的一個(gè)令人頭痛的問(wèn)題。在后面,我們還將看到建立在Runtime Unbound多態(tài)基礎(chǔ)上的更大的進(jìn)步。
    然而,盡管Runtime Unbound多態(tài)具有這些優(yōu)點(diǎn),但因?yàn)榻⒃趧?dòng)態(tài)語(yǔ)言之上,其自身存在的一些缺陷使得這項(xiàng)技術(shù)無(wú)法廣泛使用,并進(jìn)入主流。
    Runtime Unbound多態(tài)面臨的第一個(gè)問(wèn)題就是類(lèi)型安全。確切的講是靜態(tài)類(lèi)型安全。
    本質(zhì)上,Runtime Unbound多態(tài)(動(dòng)態(tài)語(yǔ)言)并非沒(méi)有類(lèi)型安全。當(dāng)動(dòng)態(tài)語(yǔ)言試圖訪問(wèn)一個(gè)未知類(lèi)型對(duì)象的成員時(shí),會(huì)通過(guò)一些特殊機(jī)制或特殊接口獲得類(lèi)型信息,并在其中尋 找所需的對(duì)象成員。如果沒(méi)有找到,便會(huì)拋出異常。但是,傳統(tǒng)上,我們希望語(yǔ)言能夠在編譯期得到類(lèi)型安全保證,而不要在運(yùn)行時(shí)才發(fā)現(xiàn)問(wèn)題。也就是說(shuō), Runtime Unbound多態(tài)只能提供運(yùn)行時(shí)類(lèi)型安全,而無(wú)法得到靜態(tài)類(lèi)型安全。
    第二個(gè)問(wèn)題是性能。Runtime Unbound需要在運(yùn)行時(shí)搜尋類(lèi)型的接口,并執(zhí)行調(diào)用。執(zhí)行這類(lèi)尋找和調(diào)用的方法有兩種:反射和動(dòng)態(tài)鏈接。
    反射機(jī)制可以向程序提供類(lèi)型的信息。通過(guò)這些信息,Runtime Unbound可以了解是否存在所需的接口函數(shù)。反射通常也提供了接口函數(shù)調(diào)用的服務(wù),允許將參數(shù)打包,并通過(guò)函數(shù)名調(diào)用。這種機(jī)制性能很差,基本上無(wú)法用于稍許密集些的操作。
    動(dòng)態(tài)鏈接則是在訪問(wèn)對(duì)象前在對(duì)象的成員函數(shù)表上查詢(xún)并獲得相應(yīng)函數(shù)的地址,填充到調(diào)用方的調(diào)用表中,調(diào)用方通過(guò)調(diào)用表執(zhí)行間接調(diào)用。這種機(jī)制相對(duì)快一些,但由于需要查詢(xún)成員函數(shù)表,復(fù)雜度基本上都在O(n)左右,無(wú)法與動(dòng)多態(tài)的O(1)調(diào)用相比。
    這些問(wèn)題的解決,依賴(lài)于一種新興的技術(shù),即concept。concept不僅很消除了類(lèi)型安全的問(wèn)題,更主要的是它大幅縮小了兩種Runtime多態(tài)的性能差距,有望使Runtime Unbound成為主流的技術(shù)。

concept

    隨著C++0x逐漸浮出水面,concept作為此次標(biāo)準(zhǔn)更新的核心部分,已經(jīng)在C++社群中引起關(guān)注。隨著時(shí)間的推移,concept的潛在作用也在不斷被發(fā)掘出來(lái)。
    concept主要用來(lái)描述一個(gè)類(lèi)型的接口和特征。通俗地講,concept描述了一組具備了共同接口的類(lèi)型。在引入concept后,C++可以對(duì)模板參數(shù)進(jìn)行約束:
    concept assignable<T> {
        T& operator=(T const&);
    }
    template<assignable T> void copy(T& a, T const& b) {
        a=b;
    }
    這表示類(lèi)型T必須有operator=的重載。如果一個(gè)類(lèi)型X沒(méi)有對(duì)operator=進(jìn)行重載,那么當(dāng)調(diào)用copy時(shí),便會(huì)引發(fā)編譯錯(cuò)誤。這使得類(lèi)型參數(shù)可以在函數(shù)使用之前便能得到檢驗(yàn),而無(wú)需等到對(duì)象被使用時(shí)。
    另一方面,concept參與到特化中后,使得操作分派更加方便:
    concept assignable<T> {
        T& operator=(T const&);
    }
    concept copyable<T> {
        T& T::copy(T const&);
    }
    template<assignable T> void copy(T& a, T const& b) {    //#1
        a=b;
    }
    template<copyable T> void copy(T& a, T const& b) {    //#2
        a.copy(b);
    }
    X x1,x2; //X支持operator=操作符
    Y y1,y2; //Y擁有copy成員函數(shù)
    copy(x1, x2);    //使用#1
    copy(y1, y2);    //使用#2
    在靜多態(tài)中,concept很好地提供了類(lèi)型約束。既然同樣是Unbound,那么concept是否同樣可以被用于Runtime Unbound?應(yīng)當(dāng)說(shuō)可以,但不是現(xiàn)有的concept。在Runtime Unbound多態(tài)中,需要運(yùn)行時(shí)的concept。
    依舊使用繪圖案例做一個(gè)演示。假設(shè)這里使用的"C++"已經(jīng)支持concept,并且也支持了運(yùn)行時(shí)的concept:

    class Rectangle

    {

        void load(xml init) {...}

        void draw(monitor dev) {...}

        ...

    };

    class Ellipse

    {

        void load(xml init) {...}

        void draw(monitor dev) {...}

        ...

    };

    ...

    concept Shape<T> {

        void T::load(xml init);

        void T::draw(monitor dev);

    }

    ...

    void DrawShapes(monitor dev, vector<Shape> const& g)

    {

        vector<IShape>::const_iterator b(g.begin()), e(g.end());

        for(; b!=e; ++b)

        {

            (*b).draw(dev);

        }

    }

    ...

    vector<Shape> vg;

    vg.push_back(Rectangle(...));

    vg.push_back(Ellipse(...));

    vg.push_back(string("xxx"));    //錯(cuò)誤,不符合Shape concept

    ...

    DrawShapes(crt, vg);

    乍看起來(lái)沒(méi)什么特別的,但是請(qǐng)注意vector<Shape>。這里使用一個(gè)concept,而不是一個(gè)具體的類(lèi)型,實(shí)例化一個(gè)模板。這里的意思是說(shuō),這個(gè)容器接受的是所有符合Shape concept的對(duì)象,類(lèi)型不同也沒(méi)關(guān)系。當(dāng)push進(jìn)vg的對(duì)象不符合Shape,便會(huì)發(fā)生編譯錯(cuò)誤。

     但是,最關(guān)鍵的東西不在這里。注意到DrawShapes函數(shù)了嗎?由于vector<Shape>中的元素類(lèi)型可能完全不同。語(yǔ)句 (*b).draw(dev);的語(yǔ)義在靜態(tài)語(yǔ)言中是非法的,因?yàn)槲覀兏緹o(wú)法在編譯時(shí)具體確定(*b)的類(lèi)型,從而鏈接正確的draw成員。而在這里, 由于我們引入了Runtime Unbound,對(duì)于對(duì)象的訪問(wèn)鏈接發(fā)生在運(yùn)行時(shí)。因此,我們便可以把不同類(lèi)型的對(duì)象存放在一個(gè)容器中。

    concept在這里起到了類(lèi)型檢驗(yàn)的作用,不符合相應(yīng)concept的對(duì)象是無(wú)法放入這個(gè)容器的,從而在此后對(duì)對(duì)象的使用的時(shí)候,也不會(huì)發(fā)生類(lèi)型失配的 問(wèn)題。這也就在動(dòng)態(tài)的機(jī)制下確保了類(lèi)型安全。動(dòng)多態(tài)確保類(lèi)型安全依靠靜態(tài)類(lèi)型。也就是所有類(lèi)型都從一個(gè)抽象接口上繼承,從而將類(lèi)型歸一化,以獲得建立在靜 態(tài)類(lèi)型系統(tǒng)之上的類(lèi)型安全。而concept的類(lèi)型安全保證來(lái)源于對(duì)類(lèi)型特征的描述,是一種非侵入的接口約束,靈活性大大高于類(lèi)型歸一化的動(dòng)多態(tài)。

    如果我們引入這樣一個(gè)規(guī)則:如果用類(lèi)型創(chuàng)建實(shí)例(對(duì)象),那么所創(chuàng)建的對(duì)象是靜態(tài)鏈接的,也就是編譯時(shí)鏈接;而用concept創(chuàng)建一個(gè)對(duì)象,那么所創(chuàng)建的對(duì)象是動(dòng)態(tài)鏈接的,也就是運(yùn)行時(shí)鏈接。

    在這條規(guī)則的作用下,下面這段簡(jiǎn)單的代碼將會(huì)產(chǎn)生非常奇妙的效果:

    class nShape

    {

    public:

        nShape(Shape g, int n) : m_graph(g), m_n(n) {}

        void setShape(Shape g) {

            m_graph=g;

        }

    private:

        Shape    m_graph;

        int        m_n;

    };

    在規(guī)則的作用下,m_graph是一個(gè)動(dòng)態(tài)對(duì)象,它的類(lèi)型只有在運(yùn)行時(shí)才能明確。但是無(wú)論什么類(lèi)型,必須滿(mǎn)足Shape concept。而m_n的類(lèi)型是確定的,所以是一個(gè)靜態(tài)對(duì)象

    這和傳統(tǒng)的模板有區(qū)別嗎?模板也可以用不同的類(lèi)型參數(shù)定義成員數(shù)據(jù)。請(qǐng)看如下代碼:

    Rectangle r;

    Ellipse e;
     nShape(r, 10);

     nShape.setShape(e);   //對(duì)于傳統(tǒng)模板而言,這個(gè)操作是非法的,因?yàn)閑和r不是同一種類(lèi)型

    動(dòng)態(tài)對(duì)象的特點(diǎn)在于,我們可以在對(duì)象創(chuàng)建后,用一個(gè)不同類(lèi)型的動(dòng)態(tài)對(duì)象代替原來(lái)的,只需要這些對(duì)象符合相應(yīng)的concept。這在靜態(tài)的模板上是做不到的。

    現(xiàn)在回過(guò)頭來(lái)看一下用concept實(shí)例化模板的情形。我們知道,用一個(gè)類(lèi)型實(shí)例化一個(gè)模板,得到的是一個(gè)類(lèi),或者說(shuō)類(lèi)型。而用一個(gè)concept實(shí)例化 一個(gè)模板,得到的又是什么呢?還是一個(gè)concept。那么vector<Shape>是一個(gè)concept,因而它的實(shí)例是動(dòng)態(tài)的對(duì)象。當(dāng) 然,實(shí)際上沒(méi)有必要把vector<Shape>的實(shí)例整個(gè)地當(dāng)成動(dòng)態(tài)對(duì)象,它只是具有動(dòng)態(tài)對(duì)象的行為特征。在實(shí)現(xiàn)上,vector< Shape>可以按照普通模板展開(kāi),而其內(nèi)部由concept模板實(shí)參定義的對(duì)象作為動(dòng)態(tài)對(duì)象處理即可。一個(gè)由concept實(shí)例化的模板的對(duì)象作為語(yǔ)義上的動(dòng)態(tài)對(duì)象。
    下面的代碼則引出了另一個(gè)重要的特性:
    vector<float> vFloat;    //靜態(tài)對(duì)象的容器,內(nèi)部存放的都是靜態(tài)對(duì)象,屬于同一類(lèi)型float
    vector<Shape> vShape; //動(dòng)態(tài)對(duì)象的容器,內(nèi)部存放動(dòng)態(tài)對(duì)象,都符合Shape
    同一個(gè)類(lèi)模板,當(dāng)使用類(lèi)型實(shí)例化,執(zhí)行static unbound多態(tài)使用concept實(shí)例化,執(zhí)行runtime unbound多態(tài)。兩者的形式相同。也就是說(shuō)static多態(tài)同runtime多態(tài)以相同的形式表達(dá)。 由于concept的加入,兩種完全不同的多態(tài)被統(tǒng)一在同一個(gè)模型和形式下。實(shí)際上,static和runtime unbound多態(tài)可以看作同一個(gè)抽象體系的兩個(gè)分支,分別處理不同情況的應(yīng)用。而形式上的統(tǒng)一,則更加接近抽象體系的本質(zhì)。同時(shí),也使得兩種 unbound多態(tài)的差異被后臺(tái)化,使用者無(wú)需額外的工作,便可以同時(shí)獲得動(dòng)態(tài)和靜態(tài)的抽象能力。同時(shí),兩種多態(tài)所展示的邏輯上的對(duì)稱(chēng)性,也暗示了兩者在 本質(zhì)上的聯(lián)系。這里統(tǒng)一的形式,便是這種對(duì)稱(chēng)性的結(jié)果。
    對(duì)于模板函數(shù),則會(huì)表現(xiàn)出更加有趣的特性(這個(gè)函數(shù)模板有些特別,不需要template關(guān)鍵字和類(lèi)型參數(shù)列表,這是我偽造的。但由于concept的使用,它本質(zhì)上還是一個(gè)模板):
    void draw(Shape g);
    這個(gè)函數(shù)接受一個(gè)符合Shape的參數(shù)。如果我們用一個(gè)靜態(tài)對(duì)象調(diào)用這個(gè)函數(shù):
    Rectangle r;
    draw(r);
    那么,就執(zhí)行static unbound,實(shí)例化成一個(gè)完完整整的函數(shù),同傳統(tǒng)的函數(shù)模板一樣。
    如果用一個(gè)動(dòng)態(tài)對(duì)象調(diào)用這個(gè)函數(shù):
    Shape g=Cycle();
    draw(g);
    g=Rectangle();
    draw(g);
    那么,就執(zhí)行runtime unbound,生成一個(gè)等待運(yùn)行時(shí)鏈接的函數(shù)。上面的兩次調(diào)用,分別進(jìn)行了兩次運(yùn)行時(shí)鏈接,以匹配不同的動(dòng)態(tài)對(duì)象。
    這樣,我們可以通過(guò)函數(shù)調(diào)用時(shí)的參數(shù)對(duì)象,來(lái)控制使用不同的多態(tài)形式。更復(fù)雜的情況就是用一個(gè)函數(shù)的返回值調(diào)用另一個(gè)函數(shù),這樣構(gòu)成的調(diào)用鏈依然符合上述的調(diào)用控制原則。
    下面,我們將看到Runtime Unbound多態(tài)的一個(gè)精彩表演:
    //假設(shè),我們已經(jīng)定義了Rectangle、Cycle、Square、Ellipse、Trangle五個(gè)類(lèi),
    // 分別map到Rectangles、Cycles、Squares、Ellipses、Trangles五個(gè)concept上,
    // 這些concept都refine(可以不正確地理解為繼承吧)自Shape。
    void draw(monitor dev, Rectangles r); //#3
    void draw(monitor dev, Cycles c);       //#4
    void draw(monitor dev, Squares s);    //#5
    void draw(monitor dev, Ellipses e);    //#6
    void draw(monitor dev, Trangles t);    //#7
    //此處定義一個(gè)Shape的動(dòng)態(tài)對(duì)象
    Shape g=CreateShapeByUserInput();    //這個(gè)函數(shù)根據(jù)用戶(hù)輸入創(chuàng)建圖形對(duì)象,所以圖形對(duì)象的類(lèi)型只能到運(yùn)行時(shí)從能確定。
    draw(crt, g);
    好了,現(xiàn)在該調(diào)用哪個(gè)版本的draw?根據(jù)用戶(hù)的輸入來(lái)。換句話(huà)說(shuō),調(diào)用哪個(gè)版本的draw,取決于CreateShapeByUserInput()函數(shù)的返回結(jié)果,也就是用戶(hù)輸入的結(jié)果。如果CreateShapeByUserInput() 返回Rectangle的動(dòng)態(tài)對(duì)象,那么執(zhí)行#3;如果返回的是Trangle對(duì)象,那么執(zhí)行#7。這是一種動(dòng)態(tài)分派的操作。在運(yùn)行時(shí)concept的作 用下,實(shí)現(xiàn)起來(lái)非常容易。對(duì)draw的調(diào)用最終會(huì)被轉(zhuǎn)換成一個(gè)concept需求表,來(lái)自draw函數(shù),每一項(xiàng)對(duì)應(yīng)一個(gè)函數(shù)版本,并且指明了所對(duì)應(yīng)的 concept。動(dòng)態(tài)對(duì)象上也有一個(gè)concept表,每一項(xiàng)存放了這個(gè)對(duì)象所符合的concept。用這兩個(gè)表相互匹配,可以找到g對(duì)象的 concept最匹配的那個(gè)draw版本,然后調(diào)用。
    這實(shí)際上是將重載決斷放到運(yùn)行時(shí)進(jìn)行,而concept在其中起到了匹配參數(shù)的作用。
    這樣的做法同利用rtti信息執(zhí)行類(lèi)型分派調(diào)用類(lèi)似:
    void draw_impl(monitor dev, Rectangle& r);
    void draw_impl(monitor dev, Cycle& c);
    void draw_impl(monitor dev, Square& s);
    void draw_impl(monitor dev, Ellipse& e);
    void draw_impl(monitor dev, Trangle& t);
    void draw_impl(monitor dev, Shape& g) {
        if(typeif(g)==typeid(Rectangle))
            draw_impl(dev, (Rectangle&)g);
        else if(typeif(g)==typeid(Cycle))
            draw_impl(dev, (Cycle&)g);
        ...
    }
    但是,他們卻有著天壤之別。首先,rtti分派是侵入的。如果需要增加一個(gè)圖形,需要在draw函數(shù)中增加分派代碼。而Runtime Unbound方案則只需要用新的concept重載draw函數(shù)即可。
    其次,rtti版本有多少圖形類(lèi),就需要多少if...else...,而Runtime Unbound則是一對(duì)多的。如果有幾個(gè)圖形類(lèi)內(nèi)容不同,但有相同的接口,符合同一個(gè)concept,那么只需針對(duì)concept編寫(xiě)一個(gè)函數(shù)版本即可。 比如,如果有一個(gè)特別的CycleEx類(lèi),使用外界正方形的左上角/右下角坐標(biāo)描述,正好符合Ellipses concept,那么只需將CycleEx map到Ellipses上即可,無(wú)需多加任何代碼。
    最后,rtti需要獲取類(lèi)型信息,然后做線性比較,性能無(wú)法優(yōu)化。但Runtime Unbound通過(guò)concept表的相互匹配,僅牽涉數(shù)值操作,有很大的優(yōu)化空間。
    那么這樣一種運(yùn)行時(shí)分派有什么好處呢?我們看到圖形類(lèi)上的draw函數(shù)接受一個(gè)monitor類(lèi)型參數(shù),它代表設(shè)備。如果哪一天需要向另一種設(shè)備,比如 printer,輸出圖形,那么就需要在圖形類(lèi)上增加另一個(gè)版本的draw函數(shù)。如果類(lèi)是別人開(kāi)發(fā)的,那么就增加溝通的負(fù)擔(dān)。如果類(lèi)是外來(lái)的,我們無(wú)法修 改,那么只能通過(guò)adapter之類(lèi)的笨拙手段處理。為了讓monitor之類(lèi)同圖形本身沒(méi)有關(guān)聯(lián)的東西分離,應(yīng)當(dāng)使用自由函數(shù)執(zhí)行draw操作。但普通 函數(shù)只能接受確定的類(lèi)型重載,而傳統(tǒng)的函數(shù)模板則限于編譯期使用,無(wú)法進(jìn)行運(yùn)行時(shí)分派。所以,如果能夠使用concept重載函數(shù),并且賦予 Runtime Unbound機(jī)能,那么便可以用最簡(jiǎn)單的形式針對(duì)一類(lèi)類(lèi)型進(jìn)行處理,效能高得多。

運(yùn)行時(shí)concept

   語(yǔ)言層面的concept無(wú)法做到這些,因?yàn)樗蔷幾g期機(jī)制。為此,我們需要有一種運(yùn)行時(shí)的concept,或者說(shuō)二進(jìn)制級(jí)別的concept。

    一個(gè)concept包含了與一個(gè)或若干個(gè)類(lèi)型有關(guān)的一組函數(shù),包括成員函數(shù)和自由函數(shù)。于是,我們就可以用一個(gè)類(lèi)似“虛表”的函數(shù)指針表(暫且稱(chēng)為 ctable吧)存放concept指定的函數(shù)指針。這樣的ctable依附在動(dòng)態(tài)對(duì)象上,就像vtable一樣。每個(gè)對(duì)象都會(huì)匹配和map到若干個(gè) concept。因此,每個(gè)動(dòng)態(tài)對(duì)象會(huì)有一個(gè)concept表,其中存放著指向各ctable的指針,以及相應(yīng)的concept基本信息。

    當(dāng)一個(gè)“用戶(hù)”(函數(shù)或模板)需要在運(yùn)行時(shí)鏈接到對(duì)象上的時(shí)候,它會(huì)提交一個(gè)concept的代碼(全局唯一)。系統(tǒng)用這個(gè)代碼在動(dòng)態(tài)對(duì)象的 concept表上檢索,獲得指向所需concept的指針,并且填寫(xiě)到“用戶(hù)”給出的一個(gè)“插入點(diǎn)”(一個(gè)指針)中。隨后“用戶(hù)”便可以直接通過(guò)這個(gè) “插入點(diǎn)”間接調(diào)用所需的函數(shù),成員或自由函數(shù)。

    在這里,concept的巧妙之處在于,將一族函數(shù)集合在一起,作為一個(gè)整體(即接口)。那么,在執(zhí)行運(yùn)行時(shí)匹配的時(shí)候,不再是一個(gè)函數(shù)一個(gè)函數(shù)地查詢(xún), 可以一次性地獲知這些函數(shù)是否存在。這就很容易地規(guī)避了類(lèi)型安全保證操作的損耗。如果使用hash查詢(xún),那么可以在O(1)實(shí)現(xiàn)concept匹配。另 外,一個(gè)concept的hash值可以在編譯時(shí)計(jì)算好,運(yùn)行時(shí)鏈接只需執(zhí)行hash表檢索,連hash值計(jì)算也可以省去。

    一個(gè)動(dòng)態(tài)對(duì)象可以直接用指向concept ctable的指針表示。在不同concept之間轉(zhuǎn)換,相當(dāng)于改變指針的指向,這種操作非常類(lèi)似OOP中的dynamic_cast。

    對(duì)于如下的動(dòng)態(tài)對(duì)象定義:

    Shape g=Cycle();

    會(huì)創(chuàng)建一個(gè)Cycle對(duì)象,在對(duì)象上構(gòu)建起一個(gè)concept表,表中對(duì)應(yīng)Cycle所有符合的concept。并且建立一組ctable,每個(gè) ctable對(duì)應(yīng)一個(gè)concept。每個(gè)concept表項(xiàng)指向相應(yīng)的ctable。而符號(hào)g則實(shí)際上是指向所建立對(duì)象的Shapes ctable的指針。

    對(duì)于函數(shù):

    void draw(Shape g);

    draw(g);

    調(diào)用g時(shí),由于draw的參數(shù)是Shape concept,而g正是draw所需的concept,所以無(wú)需在對(duì)象g的concept表上匹配,可以直接使用這個(gè)ctable指針。這就是說(shuō),只要 所用動(dòng)態(tài)對(duì)象(g)的concept同使用方(draw函數(shù))能夠匹配,便可以直接使用指向ctable的指針鏈接(編譯時(shí)鏈接),無(wú)需在運(yùn)行時(shí)重新匹 配。只有發(fā)生concept轉(zhuǎn)換時(shí),才需要在concept表中搜索,獲得所需的ctable指針:

    Swappable s=g; //Swappable是另一個(gè)concept

    這種情況同dynamic_cast極其相似。也可以模仿著采用concept_cast之類(lèi)的操作符,使得concept轉(zhuǎn)換顯式化,消除隱式轉(zhuǎn)換的問(wèn)題(強(qiáng)concept化)。

    所以,Runtime Unbound在運(yùn)行時(shí)concept的作用下,具有同動(dòng)多態(tài)相同的底層行為。因而,他們的性能也是一樣的。很多用于動(dòng)多態(tài)的方案和算法都可以直接用于運(yùn)行時(shí)concept。

Runtime Unbound和Runtime Bound

    對(duì)于runtime unbound同runtime bound之間的差異前面已經(jīng)有所展示。在其他方面,兩者還存在更多的差別。
    首先,就像繪圖案例中展示的那樣,runtime unbound是非侵入的。runtime unbound不要求類(lèi)型繼承自同一類(lèi)型,只需將類(lèi)型同concept關(guān)聯(lián)起來(lái)便可。
    其次,concept不是一種局限于OO的技術(shù),不僅涉及成員函數(shù),還包括了自由函數(shù),范圍更廣,更加靈活。
    最后,實(shí)現(xiàn)上,Runtime Unbound和Runtime Bound之間有驚人的相似之處。兩者都采用一個(gè)函數(shù)指針表作為操作分派;都采用一個(gè)指向函數(shù)表的指針作為入口;一個(gè)動(dòng)態(tài)對(duì)象上的concept之間的轉(zhuǎn) 換,也同動(dòng)多態(tài)對(duì)象一樣,在不同的函數(shù)表間切換。他們唯一的不同,是實(shí)現(xiàn)接口的機(jī)制。
    動(dòng)多態(tài)用類(lèi)型兼任接口,通過(guò)繼承和虛函數(shù)實(shí)現(xiàn)接口的功能。用類(lèi)型作為類(lèi)型的接口,使得這兩個(gè)本來(lái)獨(dú)立的概念交織在一起。增加整個(gè)類(lèi)型體系的復(fù)雜度和耦合度。    concept則利用獨(dú)立的系統(tǒng)描述、表達(dá)和管理接口。類(lèi)型則回歸到表達(dá)業(yè)務(wù)對(duì)象的功能上來(lái)。
    動(dòng)多態(tài)在使用類(lèi)型表達(dá)接口的時(shí)候,便很容易地引入一個(gè)麻煩的問(wèn)題,表達(dá)功能的類(lèi)型和表達(dá)接口的類(lèi)型混合在一起,使用時(shí)必須通過(guò)一些方法區(qū)分出哪些是接口, 哪些是功能類(lèi)型。這增加了對(duì)象模型的復(fù)雜性。而concept則獨(dú)立于類(lèi)型體系之外,所有對(duì)接口的操作都是單一的,檢索和匹配來(lái)得更加方便快捷。
    作為繼承體系的基礎(chǔ)部分,動(dòng)多態(tài)的抽象接口必須在繼承結(jié)構(gòu)的最頂端。那么這些抽象類(lèi)型必須先于其他類(lèi)型出現(xiàn)。這對(duì)系統(tǒng)的早期設(shè)計(jì)產(chǎn)生很大的壓力,往往一個(gè)基礎(chǔ)抽象接口設(shè)計(jì)有誤,便會(huì)造成整個(gè)體系的變更。
    而concept是獨(dú)立于類(lèi)型的,那么任何時(shí)候都可以將一個(gè)類(lèi)型同接口綁定。接口甚至可以在類(lèi)型體系基本建立之后才確定。這種靈活性對(duì)復(fù)雜軟件的開(kāi)發(fā)至關(guān)重要,去掉了長(zhǎng)期以來(lái)套在人們頭上的枷鎖。
    前面已經(jīng)提到,在不需要concept轉(zhuǎn)換的情況下,無(wú)需執(zhí)行運(yùn)行時(shí)的concept匹配,所有的調(diào)用具有同動(dòng)多態(tài)一樣的效率(都是間接調(diào)用)。在執(zhí)行 concept轉(zhuǎn)換時(shí),無(wú)需象動(dòng)多態(tài)那樣在復(fù)雜的繼承體系上檢索,只需執(zhí)行concept表的hash匹配,效率反而更高,而且更簡(jiǎn)單??紤]到這些情況, 我們可以認(rèn)為concept化的Runtime Unbound多態(tài)完全能夠替代傳統(tǒng)的動(dòng)多態(tài)。也就是說(shuō),我們不再需要?jiǎng)佣鄳B(tài)了
     想象一下,如果一門(mén)語(yǔ)言能夠擁有運(yùn)行時(shí)concept,那么它完全可以只保留Static Unbound和Runtime Unbound多態(tài),而放棄Runtime Bound多態(tài)。一旦放棄動(dòng)多態(tài)(沒(méi)有了虛函數(shù)和虛表),那么對(duì)象模型便可以大大簡(jiǎn)化。所有對(duì)象只需要線性分布,基類(lèi)和成員依次堆疊在一起,也沒(méi)有 vtable的干擾,對(duì)象結(jié)構(gòu)可以做到最簡(jiǎn)單。同時(shí),繼承也回歸了代碼重用的傳統(tǒng)用途。而且,對(duì)象獨(dú)立于接口存儲(chǔ),在能夠在編譯時(shí)靜態(tài)鏈接的時(shí)候,可以作 為靜態(tài)對(duì)象使用。而在需要?jiǎng)討B(tài)對(duì)象的地方,又可以很容易地轉(zhuǎn)換成動(dòng)態(tài)對(duì)象,只需要為其附上concept表和ctable。一切都簡(jiǎn)化了。對(duì)象模型也更加 容易統(tǒng)一。
    這對(duì)于很多底層開(kāi)發(fā)的程序員對(duì)于c++復(fù)雜而又混亂的對(duì)象模型難以接受。如果能夠廢除虛函數(shù),簡(jiǎn)化對(duì)象模型,那么對(duì)于這些底層開(kāi)發(fā)而言,將會(huì)帶來(lái)直接的好 處。只要確保不使用concpt定義對(duì)象、實(shí)例化模板,便可以使整個(gè)軟件執(zhí)行Static Unbound。這相當(dāng)于去掉OOP的C++。否則,就啟用Runtime Unbound,實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)。

總結(jié)

    Static Unbound和Runtime Unbound作為一對(duì)親密無(wú)間的多態(tài)技術(shù),體現(xiàn)了最完善的抽象形式。兩者各踞一方,相互補(bǔ)充,相互支援。而且兩者具有統(tǒng)一的表現(xiàn)形式,大大方便了使用, 對(duì)于軟件工程具有非凡的意義。另一方面,Runtime Bound多態(tài)作為OO時(shí)代的產(chǎn)物,體現(xiàn)了靜態(tài)類(lèi)型語(yǔ)言在運(yùn)行時(shí)多態(tài)方面的最大努力。但是,隨著運(yùn)行時(shí)concept的引入,Runtime Unbound多態(tài)自身存在的靜態(tài)類(lèi)型安全問(wèn)題和性能問(wèn)題,都能夠得到很好的解決。至此,Runtime Unbound便具備了替代Runtime Bound的實(shí)力。相信在不久的將來(lái),Runtime Bound將會(huì)逐漸步入它的黃昏。

參考

  1. http://groups.google.com/group/pongba/web/Runtime+Polymorphic+Generic +Programming.pdf。大牛人Jaakko Järvi等寫(xiě)的關(guān)于Runtime concept的文章,講解了runtime concept的概念的實(shí)現(xiàn)方法,并在ConceptC++上以庫(kù)的形式實(shí)現(xiàn)。其中使用傳統(tǒng)的動(dòng)多態(tài)實(shí)現(xiàn)runtime concept,這表明動(dòng)多態(tài)的實(shí)現(xiàn)機(jī)制同runtime concept是一致的。當(dāng)然庫(kù)的實(shí)現(xiàn)很復(fù)雜,這是“螺螄殼里做道場(chǎng)”,無(wú)奈之舉。Runtime concept還是應(yīng)當(dāng)在語(yǔ)言中first-class地實(shí)現(xiàn)。
  2. http://www.lubomir.org/academic/MinimizingCodeBloat.pdf。也是Jaakko Järvi寫(xiě)的,運(yùn)行時(shí)分派的文章。
  3. http://opensource.adobe.com/wiki/index.php/Runtime_Concepts。
  4. Inside C++ Object Model。

附錄 Runtime Concept的具體實(shí)現(xiàn)

    我們有一個(gè)concept:
    concept Shape<T>
    {
        void T::load(xml);
        void T::draw(device);
        void move(T&);
    }
    另外,還有一個(gè)代表圓的concept:
    concept Cycles<T> :
        CopyConstructable<T>,
        Assignable<T>,
        Swappable<T>,
        Shape<T>
    {
        T::T(double, double, double);
        double T::getX();
        double T::getY();
        double T::getR();
        void T::setX(double);
        void T::setY(double);
        void T::setR(double);
    }
    現(xiàn)在有類(lèi)型Cycle:
    class Cycle
    {
    public:
        Cycle(double x, double y, double r);
        Cycle(Cycle const& c);
        Cycle& operator=(Cycle const& c);
        void swap(Cycle const& c);
        void load(xml init);
        void draw(device dev);
        double getX();
        double getY();
        double getR();
        void setX(double x);
        void setY(double y);
        void setR(double r);
    private:
        ...
    };
    當(dāng)定義一個(gè)動(dòng)態(tài)對(duì)象:
    Shape g=Cycle();
    便會(huì)形成如下圖的結(jié)構(gòu):

    g實(shí)際上是一個(gè)指針,指向concept表的Shape項(xiàng),而Shape項(xiàng)指向Shape對(duì)應(yīng)的ctable。由于Cycle refine自Shape等眾多concept,那么Cycle的ctable實(shí)際上包含了這些concept的ctable,所以只需一個(gè)Cycle的 ctable,而其他concept都分別指向其中各自相應(yīng)的部分。ctable中的每一個(gè)項(xiàng)則指向具體的函數(shù)體。
    如果遇到語(yǔ)句:
    Swappable h=concept_cast<Swappable>(g);
    那么,將會(huì)執(zhí)行一個(gè)搜索,用concept Swappable的id(比如hash碼)在concept表中檢索是否存在Swappable項(xiàng)。如果存在,就將對(duì)應(yīng)項(xiàng)的指針賦給h。這種操作同 dynamic_cast操作非常相似,只是相比在復(fù)雜的對(duì)象結(jié)構(gòu)中查詢(xún)更加簡(jiǎn)單迅速。
    concept表置于對(duì)象的頭部或尾部,這是為了便于對(duì)象檢索concept接口。每個(gè)類(lèi)型的ctable只需一份。
    對(duì)象本體可以很容易地同concept表分離,在完全靜態(tài)的情況下,concept表是不需要的。如果需要runtime多態(tài),加上concept表即可。

posted on 2007-12-06 17:20 longshanks 閱讀(4260) 評(píng)論(12)  編輯 收藏 引用

Feedback

# re: OOP的黃昏 2007-12-06 18:05 LOGOS
concept真是好東西啊
不過(guò)這個(gè)語(yǔ)言級(jí)的新特性,還要等兩年啊  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-06 20:14 jfish
偶的C++功力太淺,這么高級(jí)的東西,收藏了慢慢研究,呵呵,concept確實(shí)不錯(cuò)  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-06 22:16 萬(wàn)連文
物極必反,或許我老了。  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-07 10:28 夢(mèng)在天涯
oop本身有虛函數(shù)和重載,我們就可以認(rèn)為是運(yùn)行時(shí)多態(tài)和編譯時(shí)多態(tài)。

后來(lái)高手又使用template和template的特化,整出一個(gè)編譯時(shí)多態(tài)。

上面的這2個(gè)倒是還好理解,現(xiàn)在有真?zhèn)€。。。多態(tài)

要用concept來(lái)實(shí)現(xiàn),但是我看concept的作用其實(shí)就是一個(gè)接口,一個(gè)抽象。(感覺(jué)跟以前沒(méi)有什么大的差別哦,也許是我沒(méi)有仔細(xì)看哦!希望高手過(guò)來(lái)指點(diǎn)哦)  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-07 15:21 abware
相對(duì)于接口來(lái)說(shuō),Concept提供了另一個(gè)維度的抽象方法,不過(guò)效果如何有待檢驗(yàn)。
  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏[未登錄](méi) 2007-12-07 18:31 hdqqq
標(biāo)題取的和內(nèi)容沒(méi)啥聯(lián)系??戳藰?biāo)題跑進(jìn)來(lái),原來(lái)在將concept。  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-10 00:02 評(píng)價(jià)名人自然只能匿了...
說(shuō)實(shí)在的, pongba僅僅是個(gè)技術(shù)宣傳者, 而云風(fēng)則過(guò)于偏執(zhí), 說(shuō)實(shí)在的, 懂得東西很多, 核心思想很菜, 他倆說(shuō)的話(huà), 那么一聽(tīng)就算了...  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-11 15:17 dananhai
受教了,不錯(cuò)不錯(cuò)啊  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2007-12-19 21:09 ffl
還是寫(xiě)的很不錯(cuò)的,OOP沒(méi)不會(huì)死掉啦
不過(guò)我希望lambda特性能被加入
boost  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2008-04-18 16:55 laochai
偶然看到
受益匪淺!
自己模擬了一下
http://blog.csdn.net/cchhope/archive/2008/04/18/2304969.aspx  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏[未登錄](méi) 2008-08-07 12:14 a
成長(zhǎng)中的C++  回復(fù)  更多評(píng)論
  

# re: OOP的黃昏 2012-05-24 21:27 Richard Wei
在C++11中沒(méi)有看到concept, 不知博主有啥看法?  回復(fù)  更多評(píng)論
  


只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            狠狠色丁香久久婷婷综合_中| 午夜在线播放视频欧美| 亚洲欧美日韩中文视频| 一区二区三区视频在线播放| 欧美日韩精品一区二区三区四区 | 国产精品高清网站| 亚洲欧美一区二区三区久久| 午夜免费日韩视频| 伊人色综合久久天天五月婷| 欧美黑人在线播放| 欧美视频免费| 久久精品卡一| 欧美www视频在线观看| 宅男精品导航| 欧美专区在线| 日韩一区二区精品葵司在线| 亚洲午夜羞羞片| 影音先锋中文字幕一区| 亚洲理论在线观看| 国产亚洲激情| 亚洲国产中文字幕在线观看| 欧美视频一区| 久久久噜噜噜久久人人看| 欧美激情久久久久| 欧美一区二区三区四区夜夜大片| 久久人人爽人人爽| 亚洲欧美国产一区二区三区| 久久久久99精品国产片| 正在播放亚洲一区| 久久综合狠狠综合久久综合88 | 亚洲欧美精品| 欧美成人精品不卡视频在线观看| 亚洲欧美日韩直播| 欧美+亚洲+精品+三区| 欧美一级片久久久久久久| 欧美成人tv| 久久久亚洲高清| 国产精品www.| 亚洲黄色成人网| 在线看片第一页欧美| 亚洲一区网站| 一区二区三区四区五区在线| 久久婷婷国产麻豆91天堂| 亚洲欧美国产日韩天堂区| 欧美激情第六页| 欧美va日韩va| 国产一区二区三区视频在线观看 | 久热这里只精品99re8久| 午夜精品久久久久久久蜜桃app | 日韩视频在线你懂得| 另类激情亚洲| 久久在精品线影院精品国产| 国产美女精品一区二区三区| 一区二区三区|亚洲午夜| 亚洲国产欧美一区二区三区久久| 国语精品中文字幕| 亚洲欧美国产精品专区久久| 亚洲一区在线免费观看| 欧美日韩国内| 91久久线看在观草草青青| 亚洲激情av在线| 男人的天堂成人在线| 欧美大片在线观看一区| 亚洲国产高清高潮精品美女| 久久国产精品一区二区三区四区| 欧美一区二区黄色| 国产亚洲va综合人人澡精品| 小嫩嫩精品导航| 久久久久久久久久久成人| 一区福利视频| 欧美大片在线观看一区| 亚洲三级观看| 亚洲免费在线视频一区 二区| 国产精品成人一区二区艾草| 亚洲一区二区在线| 久久久久久久999精品视频| 精品成人久久| 欧美福利视频在线观看| 夜夜嗨av一区二区三区四区 | 国产毛片精品视频| 久久丁香综合五月国产三级网站| 久久精品视频在线播放| 亚洲国产成人午夜在线一区| 欧美成人按摩| 亚洲一区免费观看| 裸体歌舞表演一区二区| 一本色道久久| 国产精品综合网站| 久久精品国产久精国产思思| 久久精品国产999大香线蕉| 亚洲福利在线观看| 欧美日本国产在线| 亚洲特色特黄| 久久―日本道色综合久久| 1000部国产精品成人观看| 9l国产精品久久久久麻豆| 久久漫画官网| 91久久黄色| 国产精品草草| 欧美专区18| 亚洲国产福利在线| 亚洲女同性videos| 国产日韩欧美视频| 欧美成年人在线观看| 亚洲视频久久| 亚洲欧美国产视频| 亚洲日本中文字幕| 国产精品99免视看9| 久久国产精品一区二区三区| 亚洲二区三区四区| 欧美中文字幕不卡| 国产亚洲二区| 欧美四级在线观看| 久久国产一区二区| 亚洲三级网站| 久久免费国产精品| 亚洲网站视频| 国产欧美亚洲视频| 欧美系列精品| 欧美a级一区| 亚洲免费视频成人| 欧美二区视频| 欧美一区二粉嫩精品国产一线天| 亚洲国产精品久久精品怡红院| 欧美韩日精品| 久久精品99国产精品酒店日本| 欧美视频二区| 先锋亚洲精品| 日韩视频一区二区三区在线播放| 久久狠狠亚洲综合| 日韩亚洲一区在线播放| 一区在线播放| 国产日本欧美一区二区三区在线| 欧美激情精品久久久久久大尺度| 欧美一区国产一区| 亚洲视频999| 亚洲精品三级| 亚洲精品一级| 免费在线日韩av| 久久精品99久久香蕉国产色戒| 亚洲美女中文字幕| 亚洲春色另类小说| 欧美精品一卡二卡| 欧美日韩高清在线一区| 麻豆av一区二区三区| 久久狠狠婷婷| 欧美一区在线直播| 欧美一区二区视频网站| 99精品视频免费观看| 99爱精品视频| 日韩一区二区精品视频| 亚洲国产精品久久久久| 欧美二区在线播放| 欧美激情久久久久久| 麻豆精品一区二区av白丝在线| 欧美一区二区三区在线免费观看| 亚洲永久免费精品| 亚洲男人天堂2024| 午夜欧美不卡精品aaaaa| 亚洲三级色网| 亚洲欧美日韩另类| 亚洲综合精品四区| 欧美一级大片在线观看| 欧美一区二区在线| 欧美一区1区三区3区公司| 在线视频欧美日韩精品| 亚洲网站在线看| 亚洲欧美在线观看| 欧美伊人久久大香线蕉综合69| 欧美一区二区三区四区视频| 在线亚洲欧美专区二区| 久久久久久久精| 欧美国产精品v| 亚洲美女视频网| 亚洲色图综合久久| 亚洲图片欧美日产| 久久免费高清| 欧美日韩免费精品| 国产精品网站视频| 在线免费观看一区二区三区| 影音先锋亚洲电影| 性8sex亚洲区入口| 欧美成年人视频| 夜夜狂射影院欧美极品| 午夜久久美女| 久久精品123| 国产精品av久久久久久麻豆网 | 亚洲一区二区三区精品动漫| 西西人体一区二区| 欧美国产日韩a欧美在线观看| 欧美日韩另类字幕中文| 在线成人小视频| 国产精品99久久久久久www| 久久精品最新地址| 亚洲国产欧美日韩精品| 性欧美18~19sex高清播放| 欧美日韩国产在线播放网站| 国产亚洲欧美日韩日本| 一区二区激情小说| 葵司免费一区二区三区四区五区|