亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  萬字長文炸裂!手撕 STL 迭代器源碼與 traits 編程技法

萬字長文炸裂!手撕 STL 迭代器源碼與 traits 編程技法

時間:2024-02-13

喵哥技術交流群發現了很多水平很高的朋友,歡迎大家來加喵哥微信,進群一起討論計算機知識!

程序喵大人微信

大家好,我是小賀。

1. 前言

天下大事,必作于細。

源碼之前,了無秘密。

上一篇,我們剖析了 STL 空間配置器,這一篇文章,我們來學習下 STL 迭代器以及背后的 traits 編程技法。

在 STL 編程中,容器和算法是獨立設計的,容器里面存的是數據,而算法則是提供了對數據的操作,在算法操作數據的過程中,要用到迭代器,迭代器可以看做是容器和算法中間的橋梁。


2. 迭代器設計模式

為何說迭代器的時候,還談到了設計模式?這個迭代器和設計模式又有什么關系呢?

其實,在《設計模式:可復用面向對象軟件的基礎》(GOF)這本經典書中,談到了 23 種設計模式,其中就有 iterator 迭代模式,且篇幅頗大。

碰巧,筆者在研究 STL 源碼的時候,同樣的發現有 iterator 迭代器,而且還占據了一章的篇幅。

在設計模式中,關于 iterator 的描述如下:一種能夠順序訪問容器中每個元素的方法,使用該方法不能暴露容器內部的表達方式。而類型萃取技術就是為了要解決和 iterator 有關的問題的。

有了上面這個基礎,我們就知道了迭代器本身也是一種設計模式,其設計思想值得我們仔細體會。

那么 C++ STL 實現 iterator 和 GOF 介紹的迭代器實現方法什么區別呢? 那首先我們需要了解 C++ 中的兩個編程范式的概念,OOP(面向對象編程)和 GP(泛型編程)。

在 C++ 語言里面,我們可用以下方式來簡單區分一下 OOP 和 GP 


OOP:將 methods 和 datas 關聯到一起 (通俗點就是方法和成員變量放到一個類中實現),通過繼承的方式,利用虛函數表(virtual)來實現運行時類型的判定,也叫"動態多態",由于運行過程中需根據類型去檢索虛函數表,因此效率相對較低。

GP:泛型編程,也被稱為"靜態多態",多種數據類型在同一種算法或者結構上皆可操作,其效率與針對某特定數據類型而設計的算法或者結構相同, 具體數據類型在編譯期確定,編譯器承擔更多,代碼執行效率高。在 STL 中利用 GP 將 methods 和 datas 實現了分而治之。

而 C++ STL 庫的整個實現采用的就是 GP(Generic Programming),而不是 OOP(Object Oriented Programming)。而 GOF 設計模式采用的就是繼承關系實現的,因此,相對來講,C++ STL 的實現效率會相對較高,而且也更有利于維護。

在 STL 編程結構里面,迭代器其實也是一種模板 class ,迭代器在 STL 中得到了廣泛的應用,通過迭代器,容器和算法可以有機的綁定在一起,只要對算法給予不同的迭代器,比如 vector::iterator、list::iterator,std::find()  就能對不同的容器進行查找,而無需針對某個容器來設計多個版本。
這樣看來,迭代器似乎依附在容器之下,那么,有沒有獨立而適用于所有容器的泛化的迭代器呢?這個問題先留著,在后面我們會看到,在 STL 編程結構里面,它是如何把迭代器運用的爐火純青。

3. 智能指針

STL  是泛型編程思想的產物,是以泛型編程為指導而產生的。具體來說,STL 中的迭代器將范型算法 (find, count, find_if) 等應用于某個容器中,給算法提供一個訪問容器元素的工具,iterator  就扮演著這個重要的角色。

稍微看過 STL 迭代器源碼的,就明白迭代器其實也是一種智能指針,因此,它也就擁有了一般指針的所有特點—— 能夠對其進行 *-> 操作。

template<typename T>
class ListIterator {//mylist迭代器
public:
    ListIterator(T *p = 0) : m_ptr(p){} //構造函數
    T& operator*() const { return *m_ptr;}  //取值,即dereference
    T* operator->() const { return m_ptr;} //成員訪問,即member access
    //...
};

但是在遍歷容器的時候,不可避免的要對遍歷的容器內部有所了解,所以,干脆把迭代器的開發工作交給容器的設計者,如此以來,所有實現細節反而得以封裝起來不被使用者看到,這也正是為什么每一種 STL 容器都提供有專屬迭代器的緣故。

比如筆者自己實現的 list  迭代器在這里使用的好處主要有:

  • (1) 不用擔心內存泄漏(類似智能指針,析構函數釋放內存);
  • (2) 對于 list ,取下一個元素不是通過自增而是通過 next  指針來取,使用智能指針可以對自增進行重載,從而提供統一接口。

4.模板參數推導

參數推導能幫我們解決什么問題呢?

在算法中,你可能會定義一個簡單的中間變量或者設定算法的返回變量類型,這時候,你可能會遇到這樣的問題:

假如你需要知道迭代器所指元素的類型是什么,進而獲取這個迭代器操作的算法的返回類型,但是問題是 C++   沒有 typeof 這類判斷類型的函數,也無法直接獲取,那該如何是好?

注意是類型,不是迭代器的值,雖然 C++  提供了一個 typeid()  操作符,這個操作符只能獲得型別的名稱,但不能用來聲明變量。要想獲得迭代器型別,這個時候又該如何是好呢?

function template  的參數推導機制是一個不錯的方法。

例如:

如果 I  是某個指向特定對象的指針,那么在 func 中需要指針所指向對象的型別的時候,怎么辦呢?這個還比較容易,模板的參數推導機制可以完成任務,

template <class I>
inline void func(I iter) {

    func_imp(iter, *iter); // 傳入 iter 和 iter 所指的值,class 自動推導
}

通過模板的推導機制,就能輕而易舉的獲得指針所指向的對象的類型。

template <class Iclass T>
void func_imp(I iterT t) {

        T tmp; // 這里就是迭代器所指物的類別
        // ... 功能實現
}
int main() {
    int i;
    func(&i);//這里傳入的是一個迭代器(原生指針也是一種迭代器)
}

上面的做法呢,通過多層的迭代,很巧妙地導出了 T ,但是卻很有局限性,比如,我希望 func()  返回迭代器的 value type  類型返回值, 函數的 template  參數推導機制" 推導的只是參數,無法推導函數的返回值類型。萬一需要推導函數的返回值,好像就不行了,那么又該如何是好?

這就引出了下面的內嵌型別。

5. 聲明內嵌型別

上述所說的 迭代器所指對象的型別,稱之為迭代器的 value type

盡管在 func_impl  中我們可以把 T  作為函數的返回值,但是問題是用戶需要調用的是 func 。

如果在參數推導機制上加上內嵌型別 (typedef)  呢?為指定的對象類型定義一個別名,然后直接獲取,這樣來看一下實現:

template<typename T>
class MyIter {
public:
    typedef T value_type; //內嵌類型聲明
    MyIter(T *p = 0) : m_ptr(p) {}
    T& operator*() const { return *m_ptr;}
private:
    T *m_ptr;
};

//以迭代器所指對象的類型作為返回類型
//注意typename是必須的,它告訴編譯器這是一個類型
template<typename MyIter>
typename MyIter::value_type Func(MyIter iter) 
{
    return *iter;
}

int main(int argc, const  char *argv[]) {
    MyIter<intiter(new int(666));
    std::cout<<Func(iter)<<std::endl;  //print=> 666
}

上面的解決方案看著可行,但其實呢,實際上還是有問題,這里有一個隱晦的陷阱:實際上并不是所有的迭代器都是 class type ,原生指針也是一種迭代器,由于原生指針不是 class type ,所以沒法為它定義內嵌型別。

因為 func  如果是一個泛型算法,那么它也絕對要接受一個原生指針作為迭代器,下面的代碼編譯沒法通過:

int *p = new int(5);
cout<<Func(p)<<endl// error

要解決這個問題,Partial specialization (模板偏特化)就出場了。

6. Partial specialization 模板偏特化

所謂偏特化是指如果一個 class template  擁有一個以上的 template  參數,我們可以針對其中某個(或多個,但不是全部)template  參數進行特化,比如下面這個例子:

template <typename T>
class C {...}; //此泛化版本的 T 可以是任何類型
template <typename T>
class C<T*> {...}; //特化版本,僅僅適用于 T 為“原生指針”的情況,是泛化版本的限制版

所謂特化,就是特殊情況特殊處理,第一個類為泛化版本,T  可以是任意類型,第二個類為特化版本,是第一個類的特殊情況,只針對原生指針。

6.1、原生指針怎么辦?——特性 “萃取” traits

還記得前面說過的參數推導機制+內嵌型別機制獲取型別有什么問題嗎?問題就在于原生指針雖然是迭代器但不是class ,無法定義內嵌型別,而偏特化似乎可以解決這個問題。

有了上面的認識,我們再看看 STL  是如何應用的。STL  定義了下面的類模板,它專門用來“萃取”迭代器的特性,而value type  正是迭代器的特性之一:

traits  在 bits/stl_iterator_base_types.h 這個文件中:

template<class _Tp>
struct iterator_traits<_Tp*> {

    typedef ptrdiff_t difference_type;
    typedef typename _Tp::value_type value_type;
    typedef typename _Tp::pointer pointer;
    typedef typename _Tp::reference reference;
    typedef typename _Tp::iterator_category iterator_category;
};
template<typename Iterator>
struct iterator_traits {  //類型萃取機
 typedef typename Iterator::value_type value_type; //value_type 就是 Iterator 的類型型別
}

加入萃取機前后的變化:

template<typename Iterator> //萃取前
typename Iterator::value_type  func(Iterator iter) {
    return *iter;
}

//通過 iterator_traits 作用后的版本
template<typename Iterator>  //萃取后
typename iterator_traits<Iterator>::value_type  func(Iterator iter) 
    return *iter;
}

看到這里也許你會問了,這個萃取前和萃取后的 typename :iterator_traits::value_type  跟 Iterator::value_type  看起來一樣啊,為什么還要增加 iterator_traits  這一層封裝,豈不是多此一舉?

回想萃取之前的版本有什么缺陷:不支持原生指針。而通過萃取機的封裝,我們可以通過類模板的特化來支持原生指針的版本!如此一來,無論是智能指針,還是原生指針,iterator_traits::value_type 都能起作用,這就解決了前面的問題。

//iterator_traits的偏特化版本,針對迭代器是原生指針的情況
template<typename T>
struct iterator_traits<T*> {
    typedef T value_type;
};

看到這里,我們不得不佩服的 STL 的設計者們,真·秒啊!我們用下面這張圖來總結一下前面的流程:


6.2 、const 偏特化

通過偏特化添加一層中間轉換的 traits 模板 class,能實現對原生指針和迭代器的支持,有的讀者可能會繼續追問:對于指向常數對象的指針又該怎么處理呢?比如下面的例子:

iterator_traits<const int*>::value_type  // 獲得的 value_type 是 const int,而不是 int

const 變量只能初始化,而不能賦值(這兩個概念必須區分清楚)。這將帶來下面的問題:

template<typename Iterator>
typename iterator_traits<Iterator>::value_type  func(Iterator iter) 
    typename iterator_traits<Iterator>::value_type tmp; 
    tmp = *iter; // 編譯 error
}

int val = 666 ;
const int *p = &val;
func(p); // 這時函數里對 tmp 的賦值都將是不允許的

那該如何是好呢?答案還是偏特化,來看實現:

template<typename T>
struct iterator_traits<const T*> { //特化const指針
    typedef T value_type; //得到T而不是const T
}

7. traits編程技法

通過上面幾節的介紹,我們知道,所謂的 traits 編程技法無非 就是增加一層中間的模板 class,以解決獲取迭代器的型別中的原生指針問題。利用一個中間層 iterator_traits 固定了 func 的形式,使得重復的代碼大量減少,唯一要做的就是稍稍特化一下 iterator_tartis  使其支持  pointer  和  const pointer 。

#include <iostream>

template <class T>
struct MyIter {

    typedef T value_type; // 內嵌型別聲明
    T* ptr;
    MyIter(T* p = 0) : ptr(p) {}
    T& operator*() const { return *ptr; }
};
// class type
template <class T>
struct my_iterator_traits {

    typedef typename T::value_type value_type;
};
// 偏特化 1
template <class T>
struct my_iterator_traits<T*> {

    typedef T value_type;
};
// 偏特化 2
template <class T>
struct my_iterator_traits<const T*> {

    typedef T value_type;
};

// 首先詢問 iterator_traits<I>::value_type,如果傳遞的 I 為指針,則進入特化版本,iterator_traits 直接回答;如果傳遞進來的 I 為 class type,就去詢問 T::value_type.
template <class I>
typename my_iterator_traits<I>:
:value_type Func(I ite) {
    std::cout << "normal version" << std::endl;
    return *ite;
}
int main(int argc, const  char *argv[]) {
    MyIter<intite(new int(6));
    std::cout << Func(ite)<<std::endl;//print=> 6
    int *p = new int(7);
    std::cout<<Func(p)<<std::endl;//print=> 7
    const int k = 8;
    std::cout<<Func(&k)<<std::endl;//print=> 8
}

上述的過程是首先詢問 iterator_traits::value_type,如果傳遞的 I 為指針,則進入特化版本, iterator_traits 直接回答T;如果傳遞進來的 Iclass type ,就去詢問 T::value_type。

通俗的解釋可以參照下圖:

總結:核心知識點在于 模板參數推導機制+內嵌類型定義機制, 為了能處理原生指針這種特殊的迭代器,引入了偏特化機制。traits 就像一臺 “特性萃取機”,把迭代器放進去,就能榨取出迭代器的特性。

這種偏特化是針對可調用函數 func 的偏特化,想象一種極端情況,假如 func 有幾百萬行代碼,那么如果不這樣做的話,就會造成非常大的代碼污染。同時增加了代碼冗余。

8.迭代器的型別和分類

8.1 迭代器的型別

我們再來看看迭代器的型別,常見迭代器相應型別有 5 種:

  • value_type:迭代器所指對象的類型,原生指針也是一種迭代器,對于原生指針 int*,int 即為指針所指對象的類型,也就是所謂的 value_type 。

  • difference_type:用來表示兩個迭代器之間的距離,對于原生指針,STL 以 C++ 內建的 ptrdiff_t 作為原生指針的 difference_type。

  • reference_type:是指迭代器所指對象的類型的引用,reference_type 一般用在迭代器的 * 運算符重載上,如果 value_type 是 T,那么對應的 reference_type 就是 T&;如果 value_type 是 const T,那么對應的reference_type 就是 const T&。

  • pointer_type:就是相應的指針類型,對于指針來說,最常用的功能就是 operator* 和 operator-> 兩個運算符。

  • iterator_category:的作用是標識迭代器的移動特性和可以對迭代器執行的操作,從 iterator_category 上,可將迭代器分為 Input Iterator、Output Iterator、Forward Iterator、Bidirectional Iterator、Random Access Iterator 五類,這樣分可以盡可能地提高效率。

    template<typename Category,
             typename T,
             typename Distance = ptrdiff_t,
             typename Pointer = T*,
             typename Reference = T&>
    struct iterator //迭代器的定義
    {
        typedef Category iterator_category;
        typedef T value_type;
        typedef Distance difference_type;
        typedef Pointer pointer;
        typedef Reference reference;
    };

iterator class 不包含任何成員變量,只有類型的定義,因此不會增加額外的負擔。由于后面三個類型都有默認值,在繼承它的時候,只需要提供前兩個參數就可以了。這個類主要是用來繼承的,在實現具體的迭代器時,可以繼承上面的類,這樣子就不會漏掉上面的 5 個型別了。

對應的迭代器萃取機設計如下:

tempalte<typename I>
struct iterator_traits {//特性萃取機,萃取迭代器特性
    typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type value_type;
    typedef typeanme I:difference_type difference_type;
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};

//需要對型別為指針和 const 指針設計特化版本看

8.2、迭代器的分類

最后,我們來看看,迭代器型別 iterator_category 對應的迭代器類別,這個類別會限制迭代器的操作和移動特性。除了原生指針以外,迭代器被分為五類:

  • Input Iterator:此迭代器不允許修改所指的對象,是只讀的。支持 ==、!=、++、*、-> 等操作。
  • Output Iterator:允許算法在這種迭代器所形成的區間上進行只寫操作。支持 ++、* 等操作。
  • Forward Iterator:允許算法在這種迭代器所形成的區間上進行讀寫操作,但只能單向移動,每次只能移動一步。支持 Input Iterator  和 Output Iterator 的所有操作。
  • Bidirectional Iterator:允許算法在這種迭代器所形成的區間上進行讀寫操作,可雙向移動,每次只能移動一步。支持 Forward Iterator 的所有操作,并另外支持 –  操作。
  • Random Access Iterator:包含指針的所有操作,可進行隨機訪問,隨意移動指定的步數。支持前面四種  Iterator  的所有操作,并另外支持 [n] 操作符等操作。

那么,看到這里,小賀想問大家,為什么我們要對迭代器進行分類呢?迭代器在具體的容器里是到底如何運用的呢?這個問題就放到下一節在講。

最最后,我們再來回顧一下六大組件的關系:

這六大組件的交互關系:

container(容器) 通過 allocator(配置器) 取得數據儲存空間,algorithm(算法)通過 iterator(迭代器)存取 container(容器) 內容,functor(仿函數) 可以協助 algorithm(算法) 完成不同的策略變化,adapter(配接器) 可以修飾或套接 functor(仿函數)。

9.本文小結

STL 源碼本身博大精深,還有很多精妙的設計等著大家去探索。

小賀本人才疏學淺,在這里也只是在自己掌握的程度下寫出自己的理解,不足之處希望對大家多多指出,互相討論學習。

這篇又肝到凌晨的文章,希望得到各位小伙伴的支持和鼓勵,當然了,任何建議和批評都會虛心接受,如果反饋還不錯的話,后面有時間會繼續更新類型的圖解文章。

參考文章:

《STL源碼剖析-侯捷》

https://zhuanlan.zhihu.com/p/85809752

https://wendeng.github.io

10.結尾

如果覺得文章對你有幫助,歡迎分享給你的朋友,也給小賀點個「在看」,這對小賀非常重要,謝謝你們,給各位小姐姐小哥哥們比心了

我是 herongwei ,是男人,就該對自己狠一點,祝大家工作愉快,我們下期見。

往期推薦


1、少寫點
if-else吧,它的效率有多低你知道嗎?
2、年度原創好文匯總
3、深度好文|面試官:進程和線程,我只問這19個問題
4、
他來了,他來了,C+
+17新特性精華都在這了
5一文讓你搞懂設計模式
6、C++11新特性,所有知識點都在這了!





C++學習資料免費獲取方法:關注程序喵大人,后臺回復“程序喵”即可免費獲取40萬字C++進階獨家學習資料。

亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
欧美区在线观看| 国产精品亚洲一区| 欧美日韩国产欧美日美国产精品| 欧美精品aa| 最近中文字幕日韩精品| 欧美暴力喷水在线| 夜夜精品视频| 国产精品亚洲美女av网站| 亚洲欧美久久久| 韩国三级电影久久久久久| 一区二区三区免费在线观看| 国产精品男女猛烈高潮激情| 亚洲免费一级电影| 一区二区三区在线观看欧美| 欧美国产精品va在线观看| 在线亚洲激情| 在线观看亚洲| 欧美三区视频| 欧美在线观看一二区| 亚洲高清不卡在线观看| 欧美www视频| 亚洲综合导航| 亚洲丰满在线| 欧美日韩伦理在线免费| 欧美一区亚洲二区| 99国内精品| 国产亚洲精品久| 欧美日韩综合精品| 久久www免费人成看片高清| 亚洲肉体裸体xxxx137| 国产一区二区精品久久| 欧美激情精品久久久久久大尺度| 亚洲欧美国产精品va在线观看| 伊人男人综合视频网| 欧美午夜电影完整版| 日韩午夜激情av| 在线日韩欧美| 欧美高清视频| 亚洲三级影片| 米奇777在线欧美播放| 欧美午夜激情视频| 欧美国产一区二区在线观看| 亚洲精品视频免费在线观看| 国产精品mm| 亚洲欧美日韩天堂| 亚洲欧洲日本一区二区三区| 一级日韩一区在线观看| 国产日韩精品在线观看| 欧美片在线观看| 亚洲视频一区在线观看| 在线观看亚洲a| 国产美女精品一区二区三区| 久久精品视频免费播放| 亚洲图片欧美日产| 国产精品久久久久三级| 欧美日韩亚洲不卡| 最新国产精品拍自在线播放| 你懂的视频欧美| 激情成人在线视频| 久久超碰97人人做人人爱| 亚洲乱码精品一二三四区日韩在线| 国产午夜精品美女毛片视频| 欧美日韩一区二区三区四区在线观看| 久久国产精品一区二区三区| 亚洲一本大道在线| 在线视频精品一区| 欧美成人午夜剧场免费观看| 亚洲一区日本| 欧美国产91| 亚洲视频axxx| 国产伦精品一区二区三区视频孕妇 | 国产色产综合色产在线视频| 欧美日韩精品高清| 国产精品高潮久久| 国产精品久久午夜夜伦鲁鲁| 国产精品综合av一区二区国产馆| 国产精品天美传媒入口| 国产亚洲二区| 亚洲二区在线视频| 亚洲国产日韩欧美在线图片| 亚洲精品视频中文字幕| 亚洲在线视频观看| 国产精品女人网站| 欧美天天视频| 黄色在线一区| 日韩一二在线观看| 欧美视频免费看| 欧美午夜精品久久久久久久| 欧美色图天堂网| 国产精品欧美经典| 国内精品模特av私拍在线观看| 国内视频一区| 亚洲精品午夜| 久久综合一区二区| 麻豆精品一区二区av白丝在线| 欧美91福利在线观看| 欧美日韩国产精品| 国产精品亚洲综合一区在线观看| 在线观看成人一级片| 99亚洲伊人久久精品影院红桃| 亚洲男人的天堂在线| 美脚丝袜一区二区三区在线观看| 欧美激情一区二区三区不卡| 国产欧美高清| 99视频日韩| 国产日韩欧美精品综合| 欧美日韩mp4| 亚洲视频1区| 国产婷婷一区二区| 蜜桃久久精品乱码一区二区| 国产精品久久久久久久久搜平片 | 国产精品久久久亚洲一区| 久久久91精品国产| 亚洲精品乱码久久久久久黑人| 亚洲激情午夜| 亚洲国产视频直播| 国产视频综合在线| 久久精品成人一区二区三区蜜臀 | 国产精品久久久久久久久免费樱桃 | 欧美区二区三区| 在线成人欧美| 一本色道**综合亚洲精品蜜桃冫 | 一本色道久久| 久久人人97超碰人人澡爱香蕉| 欧美午夜不卡在线观看免费 | 国产精品免费看| 影音欧美亚洲| 小黄鸭精品aⅴ导航网站入口| 欧美成人影音| 激情成人在线视频| 午夜精品亚洲| 国产精品xnxxcom| 亚洲国产欧美日韩另类综合| 亚洲影院免费| 国产精品网红福利| 亚洲天堂久久| 亚洲第一福利社区| 欧美一级黄色网| 91久久精品网| 99在线精品视频| 亚洲精品综合久久中文字幕| 久久另类ts人妖一区二区| 亚洲综合视频网| 欧美日本成人| 亚洲精品欧美激情| 久久综合狠狠| 一区二区三区在线免费视频| 亚洲欧美中文在线视频| 国产精品第三页| 99亚洲伊人久久精品影院红桃| 欧美人与性动交cc0o| 日韩一区二区福利| 欧美性大战久久久久久久蜜臀| 黑人一区二区三区四区五区| 久久国内精品自在自线400部| 国产亚洲欧美日韩美女| 久久久久久久国产| 亚洲欧洲精品成人久久奇米网| 欧美肥婆在线| 日韩视频在线一区二区三区| 欧美体内谢she精2性欧美| 亚洲一区日韩在线| 国产一区视频网站| 久久九九有精品国产23| 欧美在线免费一级片| 久久国产精品高清| 国产精品久久午夜| 亚洲国产中文字幕在线观看| 久久青草久久| 亚洲国产午夜| 欧美色123| 久久久久久久久久久久久久一区| 亚洲电影在线| 欧美成人在线网站| 亚洲欧美伊人| 亚洲精品国产日韩| 国产伦精品免费视频| 男人的天堂亚洲| 久久精品国产99国产精品| 亚洲国产欧美日韩另类综合| 国产精品亚洲аv天堂网| 欧美高清视频| 久久国产福利国产秒拍| 99riav1国产精品视频| 国产午夜精品久久久久久久| 免费成人高清在线视频| 亚洲一区二区免费视频| 亚洲巨乳在线| 国产日韩欧美麻豆| 欧美日韩精品福利| 欧美夜福利tv在线| 亚洲精品日韩在线| 国产一级一区二区| 欧美日韩在线视频观看| 久久久久久久久综合| 亚洲女性喷水在线观看一区| 亚洲人体偷拍| 一区二区三区在线视频播放| 国产精品乱码妇女bbbb| 欧美精品一线|