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

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)。就此諸老大各自提出高見,受益良多啊。經(jīng)過(guò)討論,我基本上理解了pongba的想法。但我覺(jué)得等待計(jì)算機(jī)的性能提高太消極了。我相信隨著編程技術(shù)的發(fā)展,這種最優(yōu)抽象造成的性能損失將會(huì)越來(lái)越小。這種途徑將會(huì)更快地讓人們接受最優(yōu)抽象形式。

     在“C++ Template”一書中,將多態(tài)總結(jié)為三種主要類型:runtime bound、static unbound和runtime unbound。其中runtime bound就是我們通常所說(shuō)的動(dòng)多態(tài),OOP的核心支柱(廣義上OOP還包括Object Base(OB,僅指類型封裝等OO的基本特性),但有時(shí)也會(huì)將OB和OOP分開,OOP單指以O(shè)O為基礎(chǔ)的動(dòng)多態(tài)。這里使用狹義的OOP含義); static unbound就是靜多態(tài),通過(guò)模板實(shí)現(xiàn)。而runtime unbound則是一種不常見的形式。早年的SmallTalk具有這種形式,現(xiàn)在的ruby也引入這種機(jī)制。
     在主流的(靜態(tài))語(yǔ)言中,我們會(huì)面臨兩種類型的多態(tài)需求:對(duì)于編譯期可以確定類型的,使用靜多態(tài),比如實(shí)例化一個(gè)容器;對(duì)于運(yùn)行期方能確定類型的,則使用 動(dòng)多態(tài)。而runtime unbound也可以用于運(yùn)行期類型決斷。于是,便有了兩種運(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)   

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

    class IShape

    {

        virtual void load(xml init)=0;

        virtual void draw(monitor m)=0;

        ...

    };

    然后定義各種圖形類,并從這個(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)在可以使用這些圖形類了:

    vector<IShape*> vg;

    vg.push_back(new Rectangle);

    vg.push_back(new Ellipse);

    ...

    DrawShapes(crt, vg);

    通過(guò)接口IShape,我們可以把不同的圖形類統(tǒng)一到一種類型下。但是,通過(guò)虛函數(shù)的override,由圖形類實(shí)現(xiàn)IShape上的虛函數(shù)。這可以算老 生常談了。動(dòng)多態(tài)的核心就是利用override和late bound的組合,使得一個(gè)基類可以在類型歸一化的情況下,擁有繼承類的語(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)的特征,即既有類型泛化,又是運(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)有類型,這里也不需要關(guān)心類型,只要求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++”,把上面的繪圖例子重新編寫:

    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);

    圖形類不再?gòu)某橄蠼涌贗Shape繼承,而用關(guān)鍵字anything實(shí)例化vector<>模板。這個(gè)虛構(gòu)的anything關(guān)鍵字所起的作 用就是使得vector能夠接受不同類型的對(duì)象。當(dāng)DrawShapes()函數(shù)接收到存放圖形對(duì)象的容器后,遍歷每一個(gè)對(duì)象,并且調(diào)用對(duì)象上的draw ()函數(shù),而不管其類型。
    從這段代碼中,我們可以看出Runtime Unbound多態(tài)帶來(lái)的好處。所有圖形類不再需要?dú)w一化成一個(gè)類型(抽象接口)。每個(gè)類只需按照約定,實(shí)現(xiàn)load、draw等成員函數(shù)即可。也就是 說(shuō),這些圖形類解耦合了。一旦類型解耦,便賦予我們很大的自由度。最典型的情況就是,我們需要使用一個(gè)其他人開發(fā)的圖形類,并且無(wú)法修改其實(shí)現(xiàn)。此時(shí),如 果使用動(dòng)多態(tài),就很麻煩。因?yàn)楸M管這些圖形類都擁有l(wèi)oad、draw等函數(shù),但畢竟不是繼承自IShape,無(wú)法直接插入容器。必須編寫一個(gè)繼承自 IShape的適配器,作為外來(lái)圖形類的包裝,轉(zhuǎn)發(fā)對(duì)其的訪問(wèn)。表面上,我們只是減少一個(gè)接口的定義,但Runtime Unbound多態(tài)帶來(lái)的解耦有著非凡的意義。因?yàn)轭愸詈鲜冀K是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)題就是類型安全。確切的講是靜態(tài)類型安全。
    本質(zhì)上,Runtime Unbound多態(tài)(動(dòng)態(tài)語(yǔ)言)并非沒(méi)有類型安全。當(dāng)動(dòng)態(tài)語(yǔ)言試圖訪問(wèn)一個(gè)未知類型對(duì)象的成員時(shí),會(huì)通過(guò)一些特殊機(jī)制或特殊接口獲得類型信息,并在其中尋 找所需的對(duì)象成員。如果沒(méi)有找到,便會(huì)拋出異常。但是,傳統(tǒng)上,我們希望語(yǔ)言能夠在編譯期得到類型安全保證,而不要在運(yùn)行時(shí)才發(fā)現(xiàn)問(wèn)題。也就是說(shuō), Runtime Unbound多態(tài)只能提供運(yùn)行時(shí)類型安全,而無(wú)法得到靜態(tài)類型安全。
    第二個(gè)問(wèn)題是性能。Runtime Unbound需要在運(yùn)行時(shí)搜尋類型的接口,并執(zhí)行調(diào)用。執(zhí)行這類尋找和調(diào)用的方法有兩種:反射和動(dòng)態(tài)鏈接。
    反射機(jī)制可以向程序提供類型的信息。通過(guò)這些信息,Runtime Unbound可以了解是否存在所需的接口函數(shù)。反射通常也提供了接口函數(shù)調(diào)用的服務(wù),允許將參數(shù)打包,并通過(guò)函數(shù)名調(diào)用。這種機(jī)制性能很差,基本上無(wú)法用于稍許密集些的操作。
    動(dòng)態(tài)鏈接則是在訪問(wèn)對(duì)象前在對(duì)象的成員函數(shù)表上查詢并獲得相應(yīng)函數(shù)的地址,填充到調(diào)用方的調(diào)用表中,調(diào)用方通過(guò)調(diào)用表執(zhí)行間接調(diào)用。這種機(jī)制相對(duì)快一些,但由于需要查詢成員函數(shù)表,復(fù)雜度基本上都在O(n)左右,無(wú)法與動(dòng)多態(tài)的O(1)調(diào)用相比。
    這些問(wèn)題的解決,依賴于一種新興的技術(shù),即concept。concept不僅很消除了類型安全的問(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è)類型的接口和特征。通俗地講,concept描述了一組具備了共同接口的類型。在引入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;
    }
    這表示類型T必須有operator=的重載。如果一個(gè)類型X沒(méi)有對(duì)operator=進(jìn)行重載,那么當(dāng)調(diào)用copy時(shí),便會(huì)引發(fā)編譯錯(cuò)誤。這使得類型參數(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很好地提供了類型約束。既然同樣是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è)具體的類型,實(shí)例化一個(gè)模板。這里的意思是說(shuō),這個(gè)容器接受的是所有符合Shape concept的對(duì)象,類型不同也沒(méi)關(guān)系。當(dāng)push進(jìn)vg的對(duì)象不符合Shape,便會(huì)發(fā)生編譯錯(cuò)誤。

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

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

    如果我們引入這樣一個(gè)規(guī)則:如果用類型創(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ì)象,它的類型只有在運(yùn)行時(shí)才能明確。但是無(wú)論什么類型,必須滿足Shape concept。而m_n的類型是確定的,所以是一個(gè)靜態(tài)對(duì)象

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

    Rectangle r;

    Ellipse e;
     nShape(r, 10);

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

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

    現(xiàn)在回過(guò)頭來(lái)看一下用concept實(shí)例化模板的情形。我們知道,用一個(gè)類型實(shí)例化一個(gè)模板,得到的是一個(gè)類,或者說(shuō)類型。而用一個(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>可以按照普通模板展開,而其內(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ì)象,屬于同一類型float
    vector<Shape> vShape; //動(dòng)態(tài)對(duì)象的容器,內(nèi)部存放動(dòng)態(tài)對(duì)象,都符合Shape
    同一個(gè)類模板,當(dāng)使用類型實(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ì)稱性,也暗示了兩者在 本質(zhì)上的聯(lián)系。這里統(tǒng)一的形式,便是這種對(duì)稱性的結(jié)果。
    對(duì)于模板函數(shù),則會(huì)表現(xiàn)出更加有趣的特性(這個(gè)函數(shù)模板有些特別,不需要template關(guān)鍵字和類型參數(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è)類,
    // 分別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ù)用戶輸入創(chuàng)建圖形對(duì)象,所以圖形對(duì)象的類型只能到運(yùn)行時(shí)從能確定。
    draw(crt, g);
    好了,現(xiàn)在該調(diào)用哪個(gè)版本的draw?根據(jù)用戶的輸入來(lái)。換句話說(shuō),調(diào)用哪個(gè)版本的draw,取決于CreateShapeByUserInput()函數(shù)的返回結(jié)果,也就是用戶輸入的結(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í)行類型分派調(diào)用類似:
    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版本有多少圖形類,就需要多少if...else...,而Runtime Unbound則是一對(duì)多的。如果有幾個(gè)圖形類內(nèi)容不同,但有相同的接口,符合同一個(gè)concept,那么只需針對(duì)concept編寫一個(gè)函數(shù)版本即可。 比如,如果有一個(gè)特別的CycleEx類,使用外界正方形的左上角/右下角坐標(biāo)描述,正好符合Ellipses concept,那么只需將CycleEx map到Ellipses上即可,無(wú)需多加任何代碼。
    最后,rtti需要獲取類型信息,然后做線性比較,性能無(wú)法優(yōu)化。但Runtime Unbound通過(guò)concept表的相互匹配,僅牽涉數(shù)值操作,有很大的優(yōu)化空間。
    那么這樣一種運(yùn)行時(shí)分派有什么好處呢?我們看到圖形類上的draw函數(shù)接受一個(gè)monitor類型參數(shù),它代表設(shè)備。如果哪一天需要向另一種設(shè)備,比如 printer,輸出圖形,那么就需要在圖形類上增加另一個(gè)版本的draw函數(shù)。如果類是別人開發(fā)的,那么就增加溝通的負(fù)擔(dān)。如果類是外來(lái)的,我們無(wú)法修 改,那么只能通過(guò)adapter之類的笨拙手段處理。為了讓monitor之類同圖形本身沒(méi)有關(guān)聯(lián)的東西分離,應(yīng)當(dāng)使用自由函數(shù)執(zhí)行draw操作。但普通 函數(shù)只能接受確定的類型重載,而傳統(tǒng)的函數(shù)模板則限于編譯期使用,無(wú)法進(jìn)行運(yùn)行時(shí)分派。所以,如果能夠使用concept重載函數(shù),并且賦予 Runtime Unbound機(jī)能,那么便可以用最簡(jiǎn)單的形式針對(duì)一類類型進(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è)類型有關(guān)的一組函數(shù),包括成員函數(shù)和自由函數(shù)。于是,我們就可以用一個(gè)類似“虛表”的函數(shù)指針表(暫且稱為 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è)“用戶”(函數(shù)或模板)需要在運(yùn)行時(shí)鏈接到對(duì)象上的時(shí)候,它會(huì)提交一個(gè)concept的代碼(全局唯一)。系統(tǒng)用這個(gè)代碼在動(dòng)態(tài)對(duì)象的 concept表上檢索,獲得指向所需concept的指針,并且填寫到“用戶”給出的一個(gè)“插入點(diǎn)”(一個(gè)指針)中。隨后“用戶”便可以直接通過(guò)這個(gè) “插入點(diǎn)”間接調(diào)用所需的函數(shù),成員或自由函數(shù)。

    在這里,concept的巧妙之處在于,將一族函數(shù)集合在一起,作為一個(gè)整體(即接口)。那么,在執(zhí)行運(yùn)行時(shí)匹配的時(shí)候,不再是一個(gè)函數(shù)一個(gè)函數(shù)地查詢, 可以一次性地獲知這些函數(shù)是否存在。這就很容易地規(guī)避了類型安全保證操作的損耗。如果使用hash查詢,那么可以在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)于改變指針的指向,這種操作非常類似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之類的操作符,使得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不要求類型繼承自同一類型,只需將類型同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)用類型兼任接口,通過(guò)繼承和虛函數(shù)實(shí)現(xiàn)接口的功能。用類型作為類型的接口,使得這兩個(gè)本來(lái)獨(dú)立的概念交織在一起。增加整個(gè)類型體系的復(fù)雜度和耦合度。    concept則利用獨(dú)立的系統(tǒng)描述、表達(dá)和管理接口。類型則回歸到表達(dá)業(yè)務(wù)對(duì)象的功能上來(lái)。
    動(dòng)多態(tài)在使用類型表達(dá)接口的時(shí)候,便很容易地引入一個(gè)麻煩的問(wèn)題,表達(dá)功能的類型和表達(dá)接口的類型混合在一起,使用時(shí)必須通過(guò)一些方法區(qū)分出哪些是接口, 哪些是功能類型。這增加了對(duì)象模型的復(fù)雜性。而concept則獨(dú)立于類型體系之外,所有對(duì)接口的操作都是單一的,檢索和匹配來(lái)得更加方便快捷。
    作為繼承體系的基礎(chǔ)部分,動(dòng)多態(tài)的抽象接口必須在繼承結(jié)構(gòu)的最頂端。那么這些抽象類型必須先于其他類型出現(xiàn)。這對(duì)系統(tǒng)的早期設(shè)計(jì)產(chǎn)生很大的壓力,往往一個(gè)基礎(chǔ)抽象接口設(shè)計(jì)有誤,便會(huì)造成整個(gè)體系的變更。
    而concept是獨(dú)立于類型的,那么任何時(shí)候都可以將一個(gè)類型同接口綁定。接口甚至可以在類型體系基本建立之后才確定。這種靈活性對(duì)復(fù)雜軟件的開發(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)了
     想象一下,如果一門語(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ì)象只需要線性分布,基類和成員依次堆疊在一起,也沒(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ì)于很多底層開發(fā)的程序員對(duì)于c++復(fù)雜而又混亂的對(duì)象模型難以接受。如果能夠廢除虛函數(shù),簡(jiǎn)化對(duì)象模型,那么對(duì)于這些底層開發(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)類型語(yǔ)言在運(yùn)行時(shí)多態(tài)方面的最大努力。但是,隨著運(yùn)行時(shí)concept的引入,Runtime Unbound多態(tài)自身存在的靜態(tà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等寫的關(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寫的,運(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)在有類型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)中查詢更加簡(jiǎn)單迅速。
    concept表置于對(duì)象的頭部或尾部,這是為了便于對(duì)象檢索concept接口。每個(gè)類型的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ō)的話, 那么一聽就算了...  回復(fù)  更多評(píng)論
  

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

# re: OOP的黃昏 2007-12-19 21:09 ffl
還是寫的很不錯(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è)用戶登錄后才能發(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>
            亚洲精品1区2区| 国产精品成人午夜| 91久久精品美女高潮| 欧美国产激情二区三区| 欧美国产三区| 亚洲天堂av在线免费| 性久久久久久久久久久久| 一区二区视频免费完整版观看| 久久乐国产精品| 免费成人小视频| 亚洲一区二区在线播放| 欧美一区二区三区在线观看| 亚洲高清二区| 一本久久知道综合久久| 国产欧美69| 欧美激情在线| 国产精品永久免费视频| 免费日韩一区二区| 欧美日韩一区二区三区视频| 欧美一区二区视频在线| 久久日韩精品| 午夜精品电影| 欧美大片在线看| 久久精品一区二区国产| 一本色道**综合亚洲精品蜜桃冫| 免费一区二区三区| 欧美日韩国产一区二区| 久久婷婷国产综合精品青草| 欧美日韩一区二区视频在线| 久久香蕉国产线看观看网| 欧美精品情趣视频| 久久久久在线观看| 国产精品国内视频| 亚洲国产日韩欧美在线图片| 国产精品久久99| 亚洲国产mv| 国产一区二区三区四区五区美女 | 在线观看一区二区精品视频| 一区二区三区 在线观看视| 亚洲二区免费| 亚洲综合成人在线| 亚洲无亚洲人成网站77777| 久久午夜精品| 久久久久久综合网天天| 麻豆九一精品爱看视频在线观看免费| 欧美激情亚洲自拍| 亚洲国产cao| 亚洲成人在线网| 久久久久国色av免费看影院| 欧美在线网址| 国产酒店精品激情| 亚洲一区二区三区色| 亚洲字幕在线观看| 欧美日韩国产首页在线观看| 久久一区二区三区四区| 国产精品一区一区三区| 日韩一级二级三级| 9人人澡人人爽人人精品| 久久一二三区| 久久综合久久综合久久| 国产精品综合不卡av| 日韩视频一区二区在线观看| 亚洲精品之草原avav久久| 老巨人导航500精品| 美女精品自拍一二三四| 国产亚洲毛片| 先锋a资源在线看亚洲| 亚洲女人天堂成人av在线| 欧美日韩国产一级片| 亚洲精品一区二区三| 亚洲少妇中出一区| 国产精品成人av性教育| 亚洲欧美久久久| 久久久久久久999| 亚洲成色www久久网站| 美女国产一区| 99精品国产福利在线观看免费| 中文一区二区在线观看| 国产精品日本一区二区| 久久成人18免费网站| 欧美1级日本1级| 99国产精品99久久久久久| 欧美视频亚洲视频| 香蕉久久a毛片| 欧美成人久久| 亚洲一级电影| 激情小说亚洲一区| 欧美激情片在线观看| 亚洲影院污污.| 欧美不卡视频一区| 免费成人网www| 亚洲精品视频中文字幕| 亚洲视频1区| 国产视频亚洲精品| 欧美成人有码| 亚洲视频电影在线| 美女精品在线观看| 亚洲欧美日本另类| 黄色成人在线网址| 国产精品成人播放| 美腿丝袜亚洲色图| 亚洲欧美一区二区三区久久| 亚洲电影av在线| 欧美一区二区视频免费观看| 亚洲三级色网| 国语对白精品一区二区| 欧美日韩综合不卡| 美日韩精品视频| 久久av一区二区三区漫画| 亚洲免费精品| 欧美韩国一区| 久久久亚洲综合| 亚洲女同同性videoxma| 亚洲精品影视在线观看| 精品91视频| 国产美女精品一区二区三区| 欧美日韩精品免费看| 久久综合色天天久久综合图片| 亚洲在线观看视频网站| 日韩亚洲欧美成人| 亚洲电影av在线| 男女精品网站| 久久在精品线影院精品国产| 欧美亚洲在线观看| 亚洲综合首页| 亚洲一级特黄| 一区二区欧美亚洲| 日韩天堂在线观看| 亚洲经典在线看| 在线播放不卡| 精品91在线| 在线看片欧美| 亚洲福利视频三区| 亚洲高清视频中文字幕| 伊人成综合网伊人222| 国产亚洲欧美激情| 国产一区久久久| 国产亚洲欧美激情| 黄色精品一区| 在线成人h网| 亚洲国产三级在线| 亚洲精品国产精品国自产观看浪潮| 狠狠色狠狠色综合| **欧美日韩vr在线| 亚洲精品一区二区三区樱花| 亚洲精品一区在线观看香蕉| 一区二区精品| 午夜精品影院在线观看| 久久久99国产精品免费| 久久综合一区二区| 欧美激情精品久久久久| 亚洲人成网站在线观看播放| 日韩视频免费观看高清完整版| 日韩视频在线一区二区三区| 一本到高清视频免费精品| 亚洲性感激情| 久久精品亚洲国产奇米99| 久久亚洲一区| 欧美日本视频在线| 国产精品美女视频网站| 国产一区二区三区精品欧美日韩一区二区三区 | 好吊视频一区二区三区四区| 在线不卡亚洲| 亚洲素人在线| 久久婷婷久久| 亚洲娇小video精品| 亚洲视频电影图片偷拍一区| 久久er99精品| 欧美大胆成人| 国产美女精品一区二区三区| 在线日本成人| 午夜国产一区| 亚洲丶国产丶欧美一区二区三区| 99精品国产99久久久久久福利| 先锋影音一区二区三区| 欧美国产日韩a欧美在线观看| 国产精品美女www爽爽爽视频| 狠狠色2019综合网| 亚洲视频图片小说| 免费看黄裸体一级大秀欧美| 一本色道久久综合亚洲精品婷婷| 欧美一区激情| 欧美手机在线| 亚洲欧洲在线观看| 久久久国产精品一区| 日韩午夜高潮| 久久伊人免费视频| 国产精品爽爽爽| 日韩亚洲欧美在线观看| 久久久久久97三级| 亚洲一本视频| 欧美日韩国产色综合一二三四 | 欧美激情综合在线| 伊人成综合网伊人222| 午夜久久久久久| 亚洲精品永久免费精品| 久久亚洲色图| 狠狠色2019综合网| 久久精品在线观看| 亚洲女人天堂成人av在线|