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

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

Traits: 類型的else-if-then機制

什么是traits,為什么人們把它認為是C++ Generic Programming的重要技術?

 

簡短截說,traits如此重要,是因為此項技術允許系統在編譯時根據類型作一些決斷,就好像在運行時根據值來作出決斷一樣。更進一步,此技術遵循“另增一個間接層”的諺語,解決了不少軟件工程問題,traits使您能根據其產生的背景(context)來作出抉擇。這樣最終的代碼就變得清晰易讀,容易維護。如果你正確運用了traits技術,你就能在不付出任何性能和安全代價的同時得到這些好處,或者能夠契合其他解決方案上的需求。

 

例子:Traits不僅是泛型程序設計的核心工具,而且我希望以下的例子能夠使你相信,在非常特定的問題中,它也是很有用的。假設你現在正在編寫一個關系數據庫應用程序。可能您一開始用數據庫供應商提供的API庫來進行反問數據庫的操作。但是理所當然的,不久之后你會感到不得不寫一些包裝函數來組織那些原始的API,一方面是為了簡潔,另一方面也可以更好地適應你手上的任務。這就是生活的樂趣所在,不是嗎?

 

一個典型的API是這樣的:提供一個基本的方法用來把游標(cursor, 一個行集和或者查詢結果)處的原始數據傳送到內存中?,F在我們來寫一個高級的函數,用來把某一列的值取出來,同時避免暴露底層的細節。這個函數可能會是這個樣子:

(假想的DB APIdbDB開頭)

 

// Example 1: Wrapping a raw cursor int fetch

// operation.

// Fetch an integer from the

//     cursor "cr"

//     at column "col"

//     in the value "val"

void FetchIntField(db_cursor& cr,

    unsigned int col, int& val)

{

    // Verify type match

    if (cr.column_type[col] != DB_INTEGER)

        throw std::runtime_error(

        "Column type mismatch");

    // Do the fetch

    db_integer temp;

    if (!db_Access_column(&cr, col))

        throw std::runtime_error(

        "Cannot transfer data");

    memcpy(&temp, cr.column_data[col],

        sizeof(temp));

    // Required by the DB API for cleanup

db_release_column(&cr, col);

    // Convert from the database native type to int

    val = static_cast<int>(temp);

}

 

這種接口函數我們所有人都可能不得不在某個時候寫上一遍,它不好對付但又非常重要,處理了大量細節,而且這還只是一個簡單的例子。FetchIntField抽象,提供了高一層次的功能,它能夠從游標處取得一個整數,不必再擔心那些紛繁的細節。既然這個函數如此有用,我們當然希望盡可能重用它。但是怎么做?一個很重要的泛化步驟就是讓這個函數能夠處理int之外的類型。為了做到這一點,我們得仔細 考慮代碼中跟int類型相關的部分。但首先,DB_INTEGERdb_integer是什么意思,它們是打哪兒來的?是這樣,關系數據庫供應商通常隨 API提供一些type-mapping helpers,為其所支持的每種類型和簡單的結構定義一個符號常量或者typedef,把數據庫類型對應到C/C++類型上。下面是一段假想的數據庫API頭文件:

 

#define DB_INTEGER 1

#define DB_STRING 2

#define DB_CURRENCY 3

...

typedef long int db_integer;

typedef char     db_string[255];

typedef strUCt {

    int integral_part;

    unsigned char fractionary_part;

} db_currency;

...

 

我們試著來寫一個FetchDoubleField函數,作為走向泛型化的第一步。此函數從游標處得到一個double值。數據庫本身提供的類型映像 (type mapping)db_currency,但是我們希望能用double的形式來操作。FetchDoubleField看上去跟 FetchIntField很相似,簡直就是孿生兄弟。例2

 

// Example 2: Wrapping a raw cursor double fetch operation.

//

void FetchDoubleField(db_cursor& cr, unsigned int col, double& val)

{

    if (cr.column_type[col] != DB_CURRENCY)

        throw std::runtime_error("Column type mismatch");

    if (!db_access_column(&cr, col))

        throw std::runtime_error("Cannot transfer data");

    db_currency temp;

    memcpy(&temp, cr.column_data[col], sizeof(temp));

    db_release_column(&cr, col);

    val = temp.integral_part + temp.fractionary_part / 100.;

}

 

看上去很像FetchIntField吧!我們可不想對每一個類型都寫一個單獨的函數,所以如果能夠在一個地方把FetchIntField, FetchDoubleField以及其他的Fetch函數合為一體就好了。我們把這兩片代碼的不同之處列舉如下:

 

·輸入類型:double/int

·內部類型:db_currency/db_integer

·常數值類型:DB_CURRENCY/DB_INTEGER

·算法:一個表達式/static_cast

 

輸入類型(int/double)與其他幾點之間的對應關系看上去沒什么規律可循,而是很隨意,跟數據庫供應商(恰好)提供的類型關系密切。 Template機制本身無能為力,它沒有提供如此先進的類型推理機制。也沒法把不同的類型用繼承關系組織起來,因為我們處理的是原始類型。受到API的 限制以及問題本身的底層特性,乍看上去我們好像沒轍了。不過我們還有一條活路。

 

進入TRAITS大門:Traits技術就是用來解決上述問題的:把與各種類型相關的代碼片斷合體,并且具有類似and/or結構的能力,到時可以根據不同的類型產生不同的變體。Traits依賴顯式模版特殊化(eXPlicit template specialization)機制來獲得這種結果。這一特性使你可以為每一個特定的類型提供模板類的一個單獨實現,見例3

 

// Example 3: A traits example

//

template <class T>

class SomeTemplate

{

    // generic implementation (1)

    ...

};

 

// 注意下面特異的語法

template <>

class SomeTemplate<char>

{

    // implementation tuned for char (2)

    ...

};

...

SomeTemplate<int> a;      // will use (1)

SomeTemplate<char*> b;    // will use (1)

SomeTemplate<char> c;     // will use (2)

 

如果你用char類型來實例化SomeTemplate類模板,編譯器會用那個顯式的模板聲明來特殊化。至于其他的類型,當然就用那個通用模板來實例化。這就像一個由類型驅動if-statement。通常最通用的模板(相當于else部分)最先定義,if-statement靠后一點。你甚至可以決定完全不提供通用的模板,這樣只有特定的實例化是允許的,其他的都會導致編譯錯誤。

 

現在我們把這個語言特性跟手上的問題聯系起來。我們要實現一個模板函數FetchField,用需要讀取的類型作為參數來實例化。在該函數內部,我會用一個叫做TypeId的東西代表那個符號常量,當要獲取int型 值時它的值就是DB_INTEGER,當要獲取double型值時它的值就是DB_CURRENCY。否則,就必須在編譯時報錯。類似的,根據要獲取的類型的不同,我們還需要操作不同的API類型(db_integer/db_currency)和不同的轉換算法(表達式/static_cast).讓我們用顯式模板特殊化機制來解決這個問題。我們得有一個FetchField,可以針對一個模板類來產生不同的變體,而那個模板類又能夠針對intdouble進行顯式特殊化。每個特殊化都必須為這些變體提供統一的名稱。

 

// Example 4: Defining DbTraits

//

// Most general case not implemented 最通用的情況沒有實現

template <typename T> struct DbTraits;

// Specialization for int

template <>

struct DbTraits<int>

{

    enum { TypeId = DB_INTEGER };

    typedef db_integer DbNativeType;

// 注意下面的Convertstatic member function —— 譯者

    static void Convert(DbNativeType from, int& to)

    {

        to = static_cast<int>(from);

    }

};

// Specialization for double

template <>

struct DbTraits<double>

{

    enum { TypeId = DB_CURRENCY };

    typedef db_currency DbNativeType;

    // 注意下面的Convertstatic member function —— 譯者

    static void Convert(const DbNativeType& from, double& to)

    {

        to = from.integral_part + from.fractionary_part / 100.;

    }

};

 

現在,如果你寫DbTraits<int>::TypeId,你得到的就是DB_INTEGER,而對于DbTraits<double>::TypeId,得到的就是DB_CURRENCY,對于DbTraits<anything_else>::TypeId,得到的是什么呢?Compile-time error!因為模板類本身只是聲明了,并沒有定義。是不是一勞永逸了?看看我們如何利用DbTraits來實現FetchField就放心了。我們把所有變化的部分——枚舉類型、API類型、轉換算法——都放在了DbTraits里,這下我們的函數里只包含FetchIntFieldFetchDoubleField的相同部分了:

 

// Example 5: A generic, extensible FetchField using DbTraits

//

template <class T>

void FetchField(db_cursor& cr, unsigned int col, T& val)

{

    // Define the traits type

    typedef DbTraits<T> Traits;

    if (cr.column_type[col] != Traits::TypeId)

        throw std::runtime_error("Column type mismatch");

    if (!db_access_column(&cr, col))

        throw std::runtime_error("Cannot transfer data");

    typename Traits::DbNativeType temp;

    memcpy(&temp, cr.column_data[col], sizeof(temp));

    Traits::Convert(temp, val);

    db_release_column(&cr, col);

}

 

搞定了!我們只不過實現和使用了一個traits模板類而已!Traits依靠顯式模板特殊化來把代碼中因類型不同而發生變化的片斷拖出來,用統一的接口來包裝。這個接口可以包含一個C++類所能包含的任何東西:內嵌類型,成員函數,成員變量,作為客戶的模板代碼可以通過traits模板類所公開的接口來間接訪問之。這樣的traits接口通常是隱式的,隱式接口不如函數簽名(function signatures)那么嚴格,例如,盡管DbTraits<int>::ConvertDbTraits<double>::Convert有著非常不同的簽名,但它們都可以正常工作。 Traits模板類在各種類型上建立一個統一的接口,而又針對各種類型提供不同的實現細節。由于Traits抓住了一個概念,一個相關聯的選擇集,所以能夠在相似的contexts中被重用。

 

定義: A traits template is a template class, possibly explicitly

specialized, that provides a uniform symbolic interface over a coherent

set of design choices that vary from one type to another.

      

    Traits模板是一個模板類,很可能是顯式特殊化的模板類,它為一系列根據不同類型做出的設計選擇提供了一個統一的、符號化的接口。TRAITS AS ADAPTERS: 用作適配子的TRAITS數據庫的問題說得夠多了,現在我們來看一個更一般的例子——smart pointers。假 設你正在設計一個SmartPtr模板類。對于一個smart pointer來說,最美妙的事情是它們可以自動管理內存問題,同時在其他方面又像一個常規指針。而不那么美妙的事情是,它們的實現代碼可不是好對付的,有些C++smart pointer實現技術簡直就是在黑暗中變戲法。這一殘酷的事實帶來了一個重要實踐經驗:你最好盡一切可能一勞永逸,寫出一個出色的、具有工業強度的 smart pointer來滿足你所有的需求。此外,你通常不能修改一個類來適應你的smart pointer,所以你的SmartPtr一定要足夠靈活。

 

有不少類層次使用引用計數(reference counting)以及相應的函數管理對象的生存期。然而,并沒有reference counting的標準實現方法,每一個C++庫的供應商在實現的語法和/或語義上都有所不同。例如,在你的應用程序中有這樣兩個interfaces:大部分的類實現了RefCounted接口:

class RefCounted

{

public:

    void IncRef() = 0;

    bool DecRef() = 0; // if you DecRef() to zero

        // references, the object is destroyed

        // automatically and DecRef() returns true

    virtual ~RefCounted() {}

};

 

而由第三方提供的Widget類使用不同的接口:

class Widget

{

public:

    void AddReference();

    int RemoveReference(); // returns the remaining

        // number of references; it's the client's

        // responsibility to destroy the object

    ...

};

 

不過你并不想維護兩個smart pointer類,你想讓兩種類共享一個SmartPtr。一個基于traits的解決方案把兩種不同的接口用語法和語義上統一的接口包裝起來,建立針對普通類的通用模板,而針對Widget建立一個特殊化版本,如下:

 

// Example 6: Reference counting traits

//

template <class T>

class RefCountingTraits

{

    static void Refer(T* p)

    {

        p->IncRef(); // assume RefCounted interface

    }

    static void Unrefer(T* p)

    {

        p->DecRef(); // assume RefCounted interface

    }

};

 

template <>

class RefCountingTraits<Widget>

{

    static void Refer(Widget* p)

    {

        p->AddReference(); // use Widget interface

    }

    static void Unrefer(Widget* p)

    {

        // use Widget interface

        if (p->RemoveReference() == 0)

            delete p;

    }

};

 

SmartPtr里,我們像這樣使用RefCountingTraits:

 

template <class T>

class SmartPtr

{

private:

    typedef RefCountingTraits<T> RCTraits;

    T* pointee_;

public:

    ...

    ~SmartPtr()

    {

        RCTraits::Unrefer(pointee_);

    }

};

 

當然在上面的例子里,你可能會爭論說你可以直接特殊化Widget類的SmartPtr的構造與析構函數。你可以使用把模板特殊化技術用在SmartPtr 本身,而不是用在trait上頭,這樣還可以消除額外的類。盡管對這個問題來說這種想法沒錯,但還是由一些你需要注意的缺陷:[譯者為了說明起見,提供一個針對SmartPtr本身進行顯式特殊化的范例供作者批判:]

// 通用類型版

template <class T>

class SmartPtr {

private:

    T *pointee_;

public:

    SmartPtr(T* pobj) {

pointee_ = pobj;

pobj->IncRef();

    }

    ...

    ~SmartPtr() {

pointee_->DecRef();

    }

};

 

// Widget類專用版

template <>

class SmartPtr<Widget> {

private:

    T *pointee_;

public:

    SmartPtr(T* pobj) {

pointee_ = pobj;

pobj->AddReference();

    }

    ...

    ~SmartPtr() {

if (pointee_->RemoveReference() == 0)

     delete pointee_;

    }

};

 

這么干缺乏可擴展性。如果給給SmartPtr再增加一個模板參數,喏,全完蛋了!你不能特殊    化這樣一個SmartPtr<T. U>,其中模板參數TWidget,而U可以為其他任何類型。不,你做不到。附帶說一句,smart pointer經常用作模板參數。[譯者附釋:也就是說,顯式模板特殊化的if-statement部分,針對特別的模板參數提供特別 的特殊化方案,要求定義時必須指定全部的參數類型,不能只是“部分指定”。例如:

// else-part, 針對其他所有類型

template <class T, class U>

class Demo {...};

 

template <>

class Demo<type1, type2> {...}; //可以,TU都指定了

 

template <>

class Demo<type3, U> {...}; // 不行,不能只指定部分模板參數

 

template <class U>

class Demo<type4, U> {...}; // 想蒙混過關?對不住,C++不允許]

 

·最終代碼不那么清晰。Trait有一個名字,而且把相關的東西很好的組織起來,因此使用traits的代碼更加容易理解。相比之下,用直接特殊化SmartPtr成員函數的代碼,看上去更招黑客的喜歡。

 

·你沒法對一種類型使用多種traits。

 

用繼承機制的解決方案,就算本身完美無瑕,也至少存在上述的缺陷。解決這樣一個變體問題,使用繼承實在是太笨重了。此外,通常用以取代繼承方案的另一種經典機制——containment,用在這里也顯得畫蛇添足,繁瑣不堪。相反,traits方案干凈利落,簡明有效,物合其用,恰到好處。

 

Traits的一個重要的應用是“interface glue”(接口膠合劑),通用的、可適應性極強的適配子。如果不同的類對于一個給定的概念有著不同的實現,traits可以把這些實現再組織統一成一個公共的接口。對于一個給定類型提供多種TRAITS:現在,我們假設所有的人都很喜歡你的SmartPtr模板類,直到有一天,在你的多線程應用程序里開始現了神秘的 bug,美夢被驚醒了。你發現罪魁禍首是Widget,它的引用計數函數并不是線程安全的?,F在你不得不親自實現Widget:: AddReferenceWidget::RemoveReference,最合理的位置應該是在RefCountingTraits中,打上個補丁 吧:

 

// Example 7: Patching Widget's traits for thread safety

//

template <>

class RefCountingTraits<Widget>

{

    static void Refer(Widget* p)

    {

        Sentry s(lock_); // serialize access

        p->AddReference();

}

    static void Unrefer(Widget* p)

    {

        Sentry s(lock_); // serialize access

        if (p->RemoveReference() == 0)

            delete p;

    }

private:

    static Lock lock_;

};

 

不幸的是,雖然你重新編譯、測試之后正確運行,但是程序慢得像蝸牛。仔細分析之后發現,你剛才的所作所為往程序里塞了一個糟糕的瓶頸。實際上只有少數幾個 Widget是需要能夠被好幾個線程訪問的,余下的絕大多數Widget都是只被一個線程訪問的。你要做的是告訴編譯器按你的需求分別使用多線程 traits和單線程traits這兩個不同版本。你的代碼主要使用單線程traits.如何告訴編譯器使用那個traits?這么干:把traits作為另一個模板參數傳給SmartPtr。缺省情況下傳遞老式的traits模板,而用特定的類型實例化特定的模板。

 

template <class T, class RCTraits = RefCountingTraits<T> >

class SmartPtr

{

    ...

};

 

你對單線程版的RefCountingTraits<Widget>不做改動,而把多線程版放在一個單獨的類中:

 

class MtRefCountingTraits

{

    static void Refer(Widget* p)

    {

        Sentry s(lock_); // serialize access

        p->AddReference();

    }

    static void Unrefer(Widget* p)

    {

        Sentry s(lock_); // serialize access

        if (p->RemoveReference() == 0)

            delete p;

    }

private:

    static Lock lock_;

};

 

現在你可將SmartPtr<Widget>用于單線程目的,將SmartPtr<Widget, MtRefCountingTraits>用于多線程目的。這就是了!就跟Scott Meyers可能會說的那樣,“你要是沒體會過快樂,就不知道怎么找樂?!比绻环N類型只要一個trait由可以應付,那么只要用顯式模板特殊化就夠了?,F在即使一個類型需要多個trait來應付我們也搞得定。所以,traits必須能夠從外界塞進來,而不是在內部“算出來”。一個應當謹記的慣用法是提供一個traits類作為最后一個模板參數。缺省的traits通過模板參數缺省值給定。定義:一個traits類(與traits模板類相對)或者是一個traits模板類的實例,或者是一個與traits模板類展現出相同接口的單獨的類。

 

 

posted on 2008-11-09 01:30 肥仔 閱讀(891) 評論(0)  編輯 收藏 引用 所屬分類: C++ 模板

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美一级在线播放| 国产日产欧产精品推荐色| 日韩午夜免费视频| 国产午夜精品美女毛片视频| 欧美高清视频一区| 欧美阿v一级看视频| 狂野欧美一区| 久久大逼视频| 一区二区三区国产精华| 红桃视频成人| 1769国产精品| 亚洲欧洲偷拍精品| 亚洲黄色尤物视频| 一本久久青青| 久久aⅴ国产紧身牛仔裤| 久久久久久穴| 亚洲电影在线观看| 蜜桃av一区| 亚洲精品麻豆| 欧美中文在线观看| 欧美日韩影院| 老司机午夜免费精品视频| 亚洲少妇诱惑| 午夜久久影院| 免费成人在线观看视频| 亚洲电影有码| 午夜一区不卡| 国产精品久久91| 一区二区三区中文在线观看| 亚洲美女福利视频网站| 欧美专区在线播放| 亚洲最新在线视频| 欧美成人蜜桃| 一区二区三区在线看| 亚洲欧美日韩国产一区二区三区| 蜜桃av一区二区| 久久精品30| 国产一区二区三区高清在线观看| 亚洲乱码国产乱码精品精可以看| 久久先锋影音av| 久久激情五月丁香伊人| 国产精品综合色区在线观看| 久久精品二区三区| 国产欧美一区二区三区国产幕精品| 亚洲国产精品嫩草影院| 欧美二区视频| 欧美日韩在线不卡| 亚洲影院在线| 亚洲婷婷国产精品电影人久久| 亚洲男人的天堂在线aⅴ视频| 欧美激情精品久久久久久黑人 | 午夜在线播放视频欧美| 日韩一本二本av| 国产精品专区第二| 欧美顶级大胆免费视频| 国产精品va在线播放我和闺蜜| 夜夜爽99久久国产综合精品女不卡| 亚洲福利小视频| 国产精品久久久一区二区| 欧美在线三级| 欧美日韩在线视频首页| 久久精品三级| 欧美午夜精品久久久久久人妖| 亚洲视频一区二区在线观看 | 欧美欧美天天天天操| 亚洲激情在线观看| 亚洲在线观看| 亚洲综合色丁香婷婷六月图片| 久久综合久久88| 久久精品中文字幕一区| 国产精品成人观看视频免费| 免费观看一区| 国产一区二区三区久久久久久久久| 国产情人节一区| 一区二区三区四区五区精品| 亚洲精品一区二区三区四区高清| 在线视频一区观看| 中文在线资源观看网站视频免费不卡| 欧美影院午夜播放| 久久久久在线观看| 狠狠色狠狠色综合| 久久精品理论片| 久久永久免费| 亚洲欧洲日本专区| 欧美日韩精品高清| 亚洲图片自拍偷拍| 久久成人精品一区二区三区| 国产精品腿扒开做爽爽爽挤奶网站| 亚洲免费av观看| 午夜精品久久久久久久99黑人| 国产精品午夜久久| 欧美在线三级| 欧美激情精品久久久久久大尺度| 在线观看av一区| 欧美日韩国产美女| 久久成人精品电影| 亚洲国产精品专区久久| 亚洲美女精品成人在线视频| 国产精品电影网站| 麻豆精品视频在线观看视频| 亚洲国产欧美不卡在线观看| 亚洲欧洲99久久| 亚洲美女精品一区| 极品少妇一区二区三区| 国产精品高精视频免费| 欧美承认网站| 麻豆成人综合网| 欧美一区二区三区久久精品| 欧美国产日韩在线观看| 久久精品欧美日韩| 午夜精品免费| 欧美在线观看网站| 久久只精品国产| 在线观看91精品国产麻豆| 欧美一区二区性| 中日韩高清电影网| 亚洲伦理久久| 一区二区日韩精品| 99国产精品久久久久久久| 揄拍成人国产精品视频| 国产精品久久久久av| 欧美日韩亚洲综合一区| 欧美视频一区二区在线观看| 欧美经典一区二区三区| 欧美激情在线有限公司| 久久久噜噜噜久久中文字免| 久久国产精品电影| 久久综合给合久久狠狠色 | 久久夜色精品亚洲噜噜国产mv| 香蕉久久精品日日躁夜夜躁| 久久高清国产| 欧美激情在线播放| 一本色道久久综合亚洲91| 亚洲欧美www| 美女诱惑黄网站一区| 欧美日韩国产天堂| 国内精品久久久久久久影视麻豆 | 国产精品久久久久久久久搜平片| 欧美日韩精品欧美日韩精品| 国产精品自拍三区| 一本久久精品一区二区| 久久精品视频在线| 亚洲三级影院| 久久av一区二区三区| 欧美日韩一区二区在线播放| 国产综合在线看| 一区二区三区精品视频| 欧美丝袜第一区| 亚洲国产精品久久久久婷婷884| 午夜视频久久久| 亚洲美女av在线播放| 免播放器亚洲一区| 亚洲福利视频在线| 久久久久久久综合狠狠综合| 亚洲深夜av| 国产精品免费久久久久久| 亚洲视频一区二区在线观看| 亚洲免费av片| 国产精品视频99| 久久人人爽爽爽人久久久| 午夜亚洲影视| 国产日韩欧美黄色| 国产麻豆精品在线观看| 在线综合亚洲欧美在线视频| 91久久夜色精品国产网站| 看片网站欧美日韩| 日韩午夜在线播放| 亚洲欧洲一二三| 国产精品啊v在线| 久久高清福利视频| 美脚丝袜一区二区三区在线观看 | 国产精品一区二区三区免费观看| 亚洲尤物在线| 久久一区二区三区四区五区| 91久久夜色精品国产九色| 日韩视频免费观看高清在线视频| 欧美日韩亚洲视频一区| 久久九九全国免费精品观看| 老司机一区二区| 欧美一区二区免费视频| 久久一区中文字幕| 欧美一区二区免费| 欧美二区在线观看| 欧美中文在线观看国产| 欧美剧在线观看| 另类综合日韩欧美亚洲| 欧美日韩国产精品一卡| 亚洲高清精品中出| 久久精品视频免费观看| 欧美日韩免费高清| 欧美激情网站在线观看| 伊伊综合在线| 久久国产日韩| 久久久久**毛片大全| 国产伦精品一区二区三区免费迷| 亚洲国产精品成人| 亚洲人被黑人高潮完整版| 久久久久成人精品| 久久久亚洲综合| 国产亚洲一区二区在线观看|