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

天秤座的唐風

總會有一個人需要你的分享~!- 唐風 -

  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
  13 隨筆 :: 0 文章 :: 69 評論 :: 0 Trackbacks

作者:唐風

原載:www.cnblogs.com/liyiwen

 

關于類型擦除,在網上搜出來的中文資料比較少,而且一提到類型擦除,檢索結果里就跑出很多 Java 和 C# 相關的文章來(它們實現“泛型”的方式)。所以,這一篇我打算寫得稍微詳細一點。 注意,這是一篇讀書筆記(《C++ template metaprogramming》第9.7小節和《C++ テンプレートテクニック》第七章),里面的例子都來自原書。


在 C++ 中,編譯器在編譯期進行的靜態類型檢查是比較嚴格的,但有時候我們卻希望能“避過”這樣的類型檢查,以實現更靈活的功能,同時又盡量地保持類型安全。聽起來很矛盾,而且貌似很難辦到。但其實 C++ 的庫里已經有很多這樣的應用了。比如,著名的 boost::function 和 boost::any 。當我們定義一個 function<void(int)> fun 對象,則 fun 即可以存儲函數指針,又可以存儲函數對象,注意,這兩者是不同“類型”的,而且函數對象可以是無限種類型的,但這些不同類型的“東西”都可以存在同一類型的對象 fun 中,對 fun 來說,它關心的只是存儲的“對象”是不是“可以按某種形式(如void(int))來調用”,而不關心這個“對象”是什么樣的類型。有了 function 這樣的庫,在使用回調和保存可調用“對象”的時候,我們就可以寫出更簡單且更好用的代碼來。再舉一個例子,boost::any 庫。any 可以存儲任何類型的“對象”,比如 int ,或是你自己定義的類 MyCla 的對象。這樣我們就可以使一個容器(比如 vector<boost::any> )來存儲不同類型的對象了。

這些庫所表現出來的行為,就是這篇文章中要提到的類型擦除,類型擦除可以達到下面兩個目的:

  • 用類型 S 的接口代表一系列類型 T 的的共性。
  • 如果 s 是 S 類型的變量,那么,任何 T 類型的的對象都可以賦值給s。

好了,下面我們具體地看看類型擦除是怎么回事,在這個過程中,我們先以 any 這個類為依托來解釋(因為它比較“簡單”,要解釋的額外的東西比較少)。

any 這個類需要完成的主要任務是:1. 存儲任何類型的變量 2. 可以相互拷貝 3. 可以查詢所存變量的類型信息 4. 可以轉化回原來的類型(any_cast<>)

對于其中,只要說明1和2 ,就能把類型擦除的做法展示出來了,所以,我們這里只實現一個簡單的,有1、2、3功能的any類(3是為了驗證)。

首先,寫個最簡單的“架子”出來:

class my_any { 
    ?? content_obj; 
public: 
    template <typename T> 
    my_any(T const& a_right); 
}; 

這里,由于 my_any 的拷貝構造函數使用的是模板函數,因此,我們可以任何類型的對象來初始化,并把該對象的復本保存在 content_obj 這個數據成員中。那么,問題是,content_obj 用什么類型好呢?

首先我們會想到,給 class 加個模板參數 T ,然后……,不用然后了,這樣的話,使用者需要寫這樣的代碼:

my_any<someType> x = y;

不同的 y 會創造出不同類型的 x 對象,完全不符合我們要將不同類型對象賦給同一類型對象的初衷。接著,我們會想到用 void *(C 式的泛型手法啊……),但這樣的話,我們就會完全地丟失原對象的信息,使得后面一些操作(拷貝、還原等)變得很困難,那么,再配合著加入一些變量用于保存原對象信息?你是說用類似“反射”的能力?好吧,我只好說,我以為 C++ 不存在原生的反射能力,以我淺薄的認識,我只知道像 MFC 式的侵入式手法……,嗯,此路不通。

這個困境的原因在于,在C++ 的類中,除了類模板參數之外,無法在不同的成員(函數、數據成員)之間共享類型信息。在這個例子中,content_obj 無法得知構造函數中的 T 是什么類型。所以類型無法確定。

為了妥善保存原對象復本,我們定義兩個輔助類,先上代碼(來自 boost::any 的原碼):

class placeholder 
{ 
public: // structors 
    virtual ~placeholder()      { 
    } 
public: // queries 
    virtual const std::type_info & type() const = 0; 
    virtual placeholder * clone() const = 0; 
}; 

template<typename ValueType> 
class holder : public placeholder 
{ 
public: // structors 
    holder(const ValueType & value): held(value) 
    { 
    } 
public: // queries 
    virtual const std::type_info & type() const { 
        return typeid(ValueType); 
    } 
    virtual placeholder * clone() const { 
        return new holder(held); 
    } 
public: // representation 
    ValueType held; 
}; 

首先,定義了一個基類 placeholder ,它是一個非模板的抽象類,這個抽象類的兩個接口是用來抽取對保存在 my_any 中的各種類型對象的共性的,也就是,我們需要對被保存在 my_any 中的數據進行拷貝和類型查詢。

然后用一個模板類 holder 類繼承 placeholder 類,這個(類)派生類實現了基類的虛函數,并保存了相關的數據。注意,派生類的數據成員的類型是 ValueType,也就是完整的原對象類型,由于它是個模板類,各個類成員之間可以共享類模板參數的信息,所以,可以方便地用原數據類型來進行各種操作。

有了這兩個輔助類,我們就可以這樣寫 my_any 了:

class My_any
{
    placeholder * content_obj;
public:
    template <typename T>
    My_any(T const& a_right):content_obj(new T(a_right))
    {}

    template <typename T>
    My_any & operator = (T const& a_right) {
        delete content_obj;
        content_obj = new T(a_right);
        return *this;
    }

    My_any(My_any const& a_right)
      : content_obj(a_right.content_obj ? 
          a_right.content_obj->clone() : 0)
    {        
    }

    std::type_info& type() const {
        return content_obj ? content_obj->type() : typeid(void);
    }
};

現在 my_any 類的 content_obj 的類型定義成 placeholder * ,在構造函數(和賦值運算符)中,我們使用 holder 類來生成真實的“備份”,由于 holder 是模板類,它可以根據賦值的對象相應地保存要我們需要的信息。這樣,我們就完成了在賦值的時候的“類型擦除”啦。在 my_any 的 public 接口( type() )中,利用 placeholder 的虛函數,我們就可以進行子類提供的那些操作,而子類,已經完整地保存著我們需要的原對象的信息。

接著我們看下 boost::function 中的 Type Erasure。相比起 boost::any 來,function 庫要復雜得多,因為這里只是想講 boost::function 中的“類型擦除”,而不是 boost::function 源碼剖析,所以,我們仍然本著簡化簡化再簡化的目的,只挑著討論一些“必要”的成分。

我們假設 function 不接受任何參數。為了更好的說明,我先帖代碼,再一步一步解釋,注意,下面是一片白花花的代碼,幾沒有注釋,千萬別開罵,請跳過這段代碼,后面會有分段的解釋:

#include <iostream>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/mpl/if.hpp>

using namespace std;

union any_callable {
    void (*fun_prt) (); // 函數指針
    void * fun_obj;     // 函數對象
};

template<typename Func, typename R>
struct fun_prt_manager {
    static R invoke(any_callable a_fp) {
        return reinterpret_cast<Func>(a_fp.fun_prt)();
    }
    static void destroy(any_callable a_fp) {}
};

template<typename Func, typename R>
struct fun_obj_manager {
    static R invoke(any_callable a_fo) {
        return (*reinterpret_cast<Func*>(a_fo.fun_obj))();
    }
    static void destroy(any_callable a_fo) {
        delete reinterpret_cast<Func*>(a_fo.fun_obj);
    }
};

struct funtion_ptr_tag {};
struct funtion_obj_tag {};

template <typename Func>
struct get_function_tag {
    typedef typename boost::mpl::if_<
        boost::is_pointer<Func>, // 在 VC10 中標準庫已經有它啦
        funtion_ptr_tag,
        funtion_obj_tag
    >::type FunType;
};

template <typename Signature>
class My_function;

template <typename R>
class My_function<R()> {
    R (*invoke)(any_callable);
    void (*destory)(any_callable);
    any_callable fun;
public:
    ~My_function() {
        clear();
    }

    template <typename Func>
    My_function& operator = (Func a_fun) {
        typedef typename get_function_tag<Func>::FunType fun_tag;
        assign(a_fun, fun_tag());
        return *this;
    }

    R operator () () const {
        return invoke(fun);        
    }

    template <typename T>
    void assign (T a_funPtr, funtion_ptr_tag) {
        clear();
        invoke = &fun_prt_manager<T, R>::invoke;
        destory = &fun_prt_manager<T, R>::destroy;
        fun.fun_prt = reinterpret_cast<void(*)()>(a_funPtr);
    }

    template <typename T>
    void assign (T a_funObj, funtion_obj_tag) {
        clear();
        invoke = &fun_obj_manager<T, R>::invoke;
        destory = &fun_obj_manager<T, R>::destroy;
        fun.fun_obj = reinterpret_cast<void*>(new T(a_funObj));
    }

private:
    void clear() {
        if (!destory) {
            destory(fun);
            destory = 0;
        }
    }
};


int TestFun() {
    return 0;
}

class TestFunObj {
public:
    int operator() () const {
        return 1;
    }
};

int main(int argc, char* argv[])
{
    My_function<int ()> fun;
    fun = &TestFun;
    cout<<fun()<<endl;
    fun = TestFunObj();
    cout<<fun()<<endl;    
}

首先需要考慮的是,數據成員放什么?因為我們需要存儲函數指針,也需要存儲函數對象,所以,這里定義一個聯合體:

union any_callable {
    void (*fun_prt) (); // 函數指針
    void * fun_obj;     // 函數對象
};

用來存放相應的“調用子”。另外兩個數據成員(函數指針)是為了使用上的方便,用于存儲分別針對函數指針和函數對象的相應的“操作方法”。對于函數指針和函數對象這兩者,轉型(cast)的動作都是不一樣的,所以,我們定義了兩個輔助類 fun_prt_manager 和 fun_obj_manager,它們分別定義了針對函數指針和函數對象進行類型轉換,然后再引發相應的“調用”和“銷毀”的動作。

接下來是類的兩個 assign 函數,它們針對函數針指和函數對象,分別用不同的方法來初始化類的數據成員,你看:

invoke = &fun_prt_manager<T, R>::invoke;
destory = &fun_prt_manager<T, R>::destroy;
fun.fun_prt = reinterpret_cast<void(*)()>(a_funPtr);

當 My_function 的對象是用函數指針賦值時,invoke 被 fun_prt_manager 的 static 來初始化,這樣,在“調用”時就把數據成員轉成函數指針。同理,可以知道函數對象時相應的做法(這里就不啰嗦了)。

但如何確定在進行賦值時,哪一個 assign 被調用呢?我想,熟悉 STL 的你,看到 funtion_ptr_tag 和 funtion_obj_tag 時就笑了,是的,這里的 get_function_tag 用了 type_traise 的技法,并且,配合了 boost::mpl 提供的靜態 if_ 簡化了代碼。這樣,我們就完成了賦值運算符的編寫:

    template <typename Func>
    My_function& operator = (Func a_fun) {
        typedef typename get_function_tag<Func>::FunType fun_tag;
        assign(a_fun, fun_tag());
        return *this;
    }

有了這個函數,針對函數指針和函數對象,My_function 的數據成員都可以正確的初始化了。

如我們所見,在 My_function 中,使用了很多技巧和輔助類,以使得 My_funtion 可以獲取在內部保存下函數指針或是函數對象,并在需要的時候,調用它們。函數指針或是函數對象,一旦賦值給 My_funtion,在外部看來,便失去了原來的“類型”信息,而只剩下一個共性——可以調用(callable)

這兩個例子已經向你大概展示了 C++ 的“類型擦除”,最后,再補充一下我的理解:C++中所說的“類型擦除”不是有“標準實現”的一種“技術”(像 CRTP 或是 Trais 技術那樣有明顯的實現“規律”),而更像是從使用者角度而言的一種“行為模式”。比如對于一個 boost::function 對象來說,你可以用函數指針和函數對象來對它賦值,從使用者的角度看起來,就好像在賦值的過程中,funtion pointer 和 functor 自身的類型信息被抹去了一樣,它們都被“剝離成”成了boost::function 對象的類型,只保留了“可以調用”這么一個共性,而 boost::any ,則只保留各種類型的“type查詢”和“復制”能力這兩個“共性”,其它類型信息一概抹掉。這種“類型擦除”并不是真正的語言層面擦除的,正如我們已經看到的,這一切仍然是在 C++ 的類型檢查系統中工作,維持著類型安全上的優點。

posted on 2009-12-10 23:05 唐風 閱讀(2594) 評論(9)  編輯 收藏 引用 所屬分類: 語言技術

評論

# re: C++中的類型擦除(type erasure in c++) 2009-12-11 09:00 Touchsoft
學習了……  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-11 09:14 崇文
做個標記,等看到這章的時候再來看,目前只看到第五章呢。  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-11 13:21 Vitacy.Tan
union any_callable {
void (*fun_prt) (); // 函數指針
void * fun_obj; // 函數對象
};
不用分開對待,直接void * fun就行了.函數可以直接當對象使用.
template <typename R,typename F>
class callable
{
public:
static R call(void * fun){
return (*static_cast<F *>(fun))();
}
static void destory(void *fun){
delete static_cast<F*>(fun);
}
};
template <typename R>
class function
{
public:
R operator () (){
return (*m_call)(m_fun);
}
template <typename T>
void operator=(T fun){
m_fun = static_cast<void *>(new T(fun));
m_call=callable<R,T>::call;
m_destory=callable<R,T>::destory;
}
template <typename T>
R call(){
(*static_cast<T *>(m_fun))();
}
~function(){
(*m_destory)(m_fun);
}
private:
void * m_fun;
R (*m_call)(void *);
void (*m_destory)(void *);
};
  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-12 10:50 唐風
@Vitacy.Tan
嗯,試過了你的代碼,確實在我的例子的情況下,你的做法沒有問題而且更簡潔。呵,說真的,第一次看到 new 一個 function pointer 對象,有點詫異。后來想通了,function pointer 也不過就是一個 pointer 而已。
但 boost::function 中確實對兩者進行了區分對待,而且考慮了更多的情況情況:
/**
       * A buffer used to store small function objects in
       * boost::function. It is a union containing function pointers,
       * object pointers, and a structure that resembles a bound
       * member function pointer.
       
*/

      union function_buffer
      
{
        
// For pointers to function objects
        mutable void* obj_ptr;

        
// For pointers to std::type_info objects
        struct type_t {
          
// (get_functor_type_tag, check_functor_type_tag).
          const BOOST_FUNCTION_STD_NS::type_info* type;

          
// Whether the type is const-qualified.
          bool const_qualified;
          
// Whether the type is volatile-qualified.
          bool volatile_qualified;
        }
 type;

        
// For function pointers of all kinds
        mutable void (*func_ptr)();

        
// For bound member pointers
        struct bound_memfunc_ptr_t {
          
void (X::*memfunc_ptr)(int);
          
void* obj_ptr;
        }
 bound_memfunc_ptr;

        
// For references to function objects. We explicitly keep
        
// track of the cv-qualifiers on the object referenced.
        struct obj_ref_t {
          mutable 
void* obj_ptr;
          
bool is_const_qualified;
          
bool is_volatile_qualified;
        }
 obj_ref;

        
// To relax aliasing constraints
        mutable char data;
      }
;

在下淺薄,不能完全明白其中的道理,還請路過高人指點。
 

 
  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-13 11:53 唐風
@Vitacy.Tan
在網上找了一下相關的資料,發現這個問題其實已經有了比較多的“討論”,還有專門論述的文章:
http://www.safercode.com/blog/2008/11/25/generic-function-pointers-in-c-and-void.html

簡而言之,就是:
不能 void* 和 void(*f)() 合并成一個,原因是對于 C 標準而言,這兩個“東西”并不保證一致,函數指針的大小可能比 void* (也就是指向數據的指針大小要大),可能會包含更多的信息,如果在這之間進行轉換的話,在某些平臺會產生未定義的行為(在 Windows 平臺下,這樣做是沒問題的,參考http://stackoverflow.com/questions/1867698/casting-a-void-pointer-data-to-a-function-pointer 中的回復)。因此,這么做是為了可移植性的考慮。

相關原文如下:
Why can’t we use void* for a Generic Function Pointer?
This is because a void* is a pointer to a generic “data” type. A void * is used to denote pointers to objects and in some systems, pointers to functions can be larger than pointers to objects. So, if you convert amongst them, you’ll lose information and hence, the situation would be undefined and implementation dependent. Most compilers won’t even warn you if you convert between them but some might error out, if you try to call such a void * to function pointer converted. But even they might fail to alert you, if you take care of typecasting the call perfectly (Enclose in parentheses before function call brackets). And then, one fine day, you’ll try to compile and run your program on one of the aforementioned systems, and then keep on wondering why your program segfaults.

Note: C++ does allow this “conditionally” which means that such a conversion is allowed but a compiler is not bound to implement this feature, which again makes its usage circumspect.

另外,我還是不太明白,function pointer 可能攜帶的其它信息是什么,呵呵,再查查嘍。

謝謝 Vitacy.Tan 的回復,讓我能更深入的了解這方面的知識。
嗯,有人能參與討論就是好啊,希望大家多指點。



  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-13 12:05 唐風
@Vitacy.Tan
話又說回來,在你的做法中,并沒有直接在 function pointer 和 void* 之間進行轉型,而是為 function pointer 創建(new)了一個對象,賦給 void*,這樣做我感覺是沒有問題的,這時候 void* 指向的仍然是一個“對象”,呵呵。
但這么做可能在“效率”上不如直接備份 function pointer 。畢竟,創建時候的 new 和調用時的轉型加最后的 delete,都比直接存 function pointer 負擔大。但這樣做獲得了代碼上的一致性,我倒是挺喜歡的,呵呵(如果確實沒有“我所不知道的問題的話”)。
  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-14 11:16 Vitacy.Tan
function pointer 可能攜帶的其它信息是什么
參數個數 類型 還有像int a(int b=1);
在C++里參數個數 類型在函數名里(指針好像也沒這個),默認參數可以重載解決
  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-17 10:45 U2U
很感謝你這篇文章,寫得很好!  回復  更多評論
  

# re: C++中的類型擦除(type erasure in c++) 2009-12-18 07:38 欲三更
恩,上面那個回復里用“指針的指針”規避對象指針和函數指針的大小不一樣這一點,挺好的。  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成在线观看| 免播放器亚洲| 亚洲欧美日韩一区二区三区在线| 韩国精品久久久999| 亚洲欧美国产制服动漫| 亚洲一级黄色片| 国产精品成人观看视频国产奇米| 亚洲综合视频1区| 欧美刺激午夜性久久久久久久| 亚洲国产精品一区二区www在线| 国内精品视频一区| 国产精品永久免费| 久久成人一区| aa国产精品| 久久久久久夜精品精品免费| 韩国av一区二区| 欧美日韩极品在线观看一区| 亚洲主播在线播放| 亚洲三级毛片| 欧美日韩一区精品| 亚洲一区二区三区高清| 欧美激情网友自拍| 91久久精品视频| 亚洲综合激情| 欧美激情视频在线播放| 亚洲免费视频中文字幕| 亚洲国产精品久久人人爱蜜臀| 欧美日本一区二区三区| 久久精品国产一区二区电影| 午夜精品久久久久影视 | 久久精品综合网| 日韩一级免费观看| 欧美黄色免费| 久久精品女人| 欧美日韩一区二区三区在线看| 亚洲精品综合久久中文字幕| 欧美激情国产日韩| 欧美激情无毛| 免费观看在线综合色| 亚洲国产欧美一区二区三区同亚洲| 国产日韩av一区二区| 国产欧美日韩精品专区| 亚洲一区日韩| 亚洲视频免费在线观看| 一区二区三区视频在线播放| 一本久久知道综合久久| 伊人狠狠色j香婷婷综合| 老司机精品福利视频| 玖玖玖免费嫩草在线影院一区| 国产精品日韩一区二区| 美女视频黄 久久| 欧美xxx成人| 欧美精品一区二区三| 国产精品久久二区| 国产曰批免费观看久久久| 香蕉久久夜色精品国产使用方法| 欧美亚洲在线播放| 美女图片一区二区| 欧美性生交xxxxx久久久| 欧美永久精品| 欧美一级久久久久久久大片| 亚洲性视频h| 亚洲欧美日韩电影| 久久综合五月| 亚洲欧洲一区二区三区久久| 亚洲一区3d动漫同人无遮挡| 亚洲精品一品区二品区三品区| 亚洲精品日产精品乱码不卡| 亚洲自拍偷拍视频| 欧美理论电影网| 极品尤物av久久免费看| 久久精品成人| 久久久五月婷婷| 国产精品理论片| 亚洲国产人成综合网站| 麻豆乱码国产一区二区三区| 亚洲小说区图片区| 欧美三级午夜理伦三级中视频| 亚洲欧洲中文日韩久久av乱码| 香蕉久久国产| 久久精品国产综合精品| 亚洲精品少妇| 欧美黄色一区| 欧美日韩综合在线| 亚洲精品综合在线| 欧美电影电视剧在线观看| 噜噜噜噜噜久久久久久91| 伊人久久噜噜噜躁狠狠躁| 欧美在线日韩在线| 亚洲国产另类久久久精品极度| 久久婷婷色综合| 欧美日本精品在线| 日韩手机在线导航| 99精品热视频只有精品10| 99国产精品久久久久久久| 国产精品成人免费| 在线视频欧美日韩| 日韩亚洲国产精品| 亚洲国内自拍| 欧美在线啊v一区| 亚洲福利视频网| 日韩视频专区| 黄色成人小视频| 久久精品72免费观看| 亚洲一区在线直播| 国产色产综合产在线视频| 亚洲一卡久久| 免费在线观看精品| 欧美精品一区二区在线播放| 亚洲欧美精品一区| 久热re这里精品视频在线6| 久久久久久久综合色一本| 国产精品乱码妇女bbbb| 免费在线日韩av| 国产日韩欧美一区二区| 亚洲另类自拍| 亚洲国产精品一区二区www| 亚洲一区www| 在线欧美影院| 欧美一区二区黄| 一本久久a久久精品亚洲| 欧美www视频| 国产综合精品| 日韩亚洲综合在线| 国产一区二区按摩在线观看| 99精品黄色片免费大全| 亚洲新中文字幕| 亚洲人成啪啪网站| 久久久久久穴| 免费观看国产成人| 亚洲第一区中文99精品| 久久久久国产精品一区三寸| 欧美一级播放| 国产精品久久久久久久久借妻| 日韩视频在线永久播放| 亚洲欧美日韩一区| 午夜一级在线看亚洲| 欧美一区二区三区四区在线观看| 国产精品久久久久久久久借妻| 亚洲性人人天天夜夜摸| 久久精品综合一区| 亚洲视频在线播放| 在线观看一区二区精品视频| 久久亚洲图片| 欧美成人精品一区| 久久久91精品| 亚洲美女视频网| 国产精品视频专区| 久久性色av| 一区二区三区免费在线观看| 欧美三级电影网| 亚洲欧美日韩在线观看a三区| 亚洲国产二区| 国产精品视频一二| 亚洲丝袜av一区| 欧美成人在线影院| 亚洲网站在线| 香蕉成人伊视频在线观看| 亚洲视频免费在线观看| 国产一区二区三区在线观看精品 | 久久国产精品一区二区| 99在线视频精品| 国产一区亚洲| 欧美视频一区二区三区| 蜜臀99久久精品久久久久久软件| 99re66热这里只有精品4| 国产免费成人av| 久久精品二区| 亚洲综合欧美日韩| 亚洲主播在线观看| a4yy欧美一区二区三区| 一区二区亚洲欧洲国产日韩| 欧美午夜宅男影院在线观看| 亚洲高清自拍| 久久久久久久久久久久久9999| 亚洲国产精品传媒在线观看 | 欧美电影免费观看| 小黄鸭视频精品导航| 一区二区国产精品| 亚洲影院色在线观看免费| 牛夜精品久久久久久久99黑人| 一区二区三区产品免费精品久久75 | 亚洲精品久久久久中文字幕欢迎你| 一本色道久久综合亚洲精品高清| 久久最新视频| 久久本道综合色狠狠五月| 亚洲欧洲三级| 国产一区二区主播在线| 欧美高清视频一区二区三区在线观看 | 欧美专区在线观看| 亚洲欧美另类在线| 一区二区视频免费完整版观看| 国产亚洲欧洲997久久综合| 国产精品青草综合久久久久99| 在线亚洲一区| 亚洲女与黑人做爰| 性欧美办公室18xxxxhd| 亚洲日本va午夜在线影院| 亚洲第一在线| 亚洲视频在线免费观看|