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

MySpace

  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  18 隨筆 :: 2 文章 :: 10 評論 :: 0 Trackbacks

如何組織編寫模板程序

前言
常遇到詢問使用模板到底是否容易的問題,我的回答是:“模板的使用是容易的,但組織編寫卻不容易”。看看我們幾乎每天都能遇到的模板類吧,如STL, ATL, WTL, 以及Boost的模板類,都能體會到這樣的滋味:接口簡單,操作復雜。

我在5年前開始使用模板,那時我看到了MFC的容器類。直到去年我還沒有必要自己編寫模板類。可是在我需要自己編寫模板類時,我首先遇到的事實卻是“傳統(tǒng)”編程方法(在*.h文件聲明,在*.cpp文件中定義)不能用于模板。于是我花費一些時間來了解問題所在及其解決方法。

本文對象是那些熟悉模板但還沒有很多編寫模板經(jīng)驗的程序員。本文只涉及模板類,未涉及模板函數(shù)。但論述的原則對于二者是一樣的。

問題的產(chǎn)生
通過下例來說明問題。例如在array.h文件中有模板類array:
// array.h
template <typename T, int SIZE>
class array
{
    T data_[SIZE];
    array (const array& other);
    const array& operator = (const array& other);
public:
    array(){};
    T& operator[](int i) {return data_[i];}
    const T& get_elem (int i) const {return data_[i];}
    void set_elem(int i, const T& value) {data_[i] = value;}
    operator T*() {return data_;}     
};           
           
然后在main.cpp文件中的主函數(shù)中使用上述模板:
// main.cpp
#include "array.h"

int main(void)
{
array<int, 50> intArray;
intArray.set_elem(0, 2);
int firstElem = intArray.get_elem(0);
int* begin = intArray;
}
       
這時編譯和運行都是正常的。程序先創(chuàng)建一個含有50個整數(shù)的數(shù)組,然后設置數(shù)組的第一個元素值為2,再讀取第一個元素值,最后將指針指向數(shù)組起點。

但如果用傳統(tǒng)編程方式來編寫會發(fā)生什么事呢?我們來看看:

將array.h文件分裂成為array.h和array.cpp二個文件(main.cpp保持不變)
// array.h       
template <typename T, int SIZE>
class array
{
      T data_[SIZE];
      array (const array& other);
      const array& operator = (const array& other);
  public:
      array(){};
      T& operator[](int i);
      const T& get_elem (int i) const;
      void set_elem(int i, const T& value);
      operator T*();     
};       
   
// array.cpp
#include "array.h"

template<typename T, int SIZE> T& array<T, SIZE>::operator [](int i)
    {
    return data_[i];
    }

template<typename T, int SIZE> const T& array<T, SIZE>::get_elem(int i) const
    {
    return data_[i];
    }

template<typename T, int SIZE> void array<T, SIZE>::set_elem(int i, const T& value)
    {
    data_[i] = value;
    }
template<typename T, int SIZE> array<T, SIZE>::operator T*()
    {
    return data_;
    }
       
編譯時會出現(xiàn)3個錯誤。問題出來了:
  為什么錯誤都出現(xiàn)在第一個地方?
  為什么只有3個鏈接出錯?array.cpp中有4個成員函數(shù)。
 
要回答上面的問題,就要深入了解模板的實例化過程。

模板實例化
程序員在使用模板類時最常犯的錯誤是將模板類視為某種數(shù)據(jù)類型。所謂類型參量化(parameterized types)這樣的術語導致了這種誤解。模板當然不是數(shù)據(jù)類型,模板就是模板,恰如其名:

  編譯器使用模板,通過更換模板參數(shù)來創(chuàng)建數(shù)據(jù)類型。這個過程就是模板實例化(Instantiation)。
  從模板類創(chuàng)建得到的類型稱之為特例(specialization)。
  模板實例化取決于編譯器能夠找到可用代碼來創(chuàng)建特例(稱之為實例化要素,
  point of instantiation)。
  要創(chuàng)建特例,編譯器不但要看到模板的聲明,還要看到模板的定義。
  模板實例化過程是遲鈍的,即只能用函數(shù)的定義來實現(xiàn)實例化。


再回頭看上面的例子,可以知道array是一個模板,array<int, 50>是一個模板實例 - 一個類型。從array創(chuàng)建array<int, 50>的過程就是實例化過程。實例化要素體現(xiàn)在main.cpp文件中。如果按照傳統(tǒng)方式,編譯器在array.h文件中看到了模板的聲明,但沒有模板的定義,這樣編譯器就不能創(chuàng)建類型array<int, 50>。但這時并不出錯,因為編譯器認為模板定義在其它文件中,就把問題留給鏈接程序處理。

現(xiàn)在,編譯array.cpp時會發(fā)生什么問題呢?編譯器可以解析模板定義并檢查語法,但不能生成成員函數(shù)的代碼。它無法生成代碼,因為要生成代碼,需要知道模板參數(shù),即需要一個類型,而不是模板本身。

這樣,鏈接程序在main.cpp 或 array.cpp中都找不到array<int, 50>的定義,于是報出無定義成員的錯誤。

至此,我們回答了第一個問題。但還有第二個問題,在array.cpp中有4個成員函數(shù),鏈接器為什么只報了3個錯誤?回答是:實例化的惰性導致這種現(xiàn)象。在main.cpp中還沒有用上operator[],編譯器還沒有實例化它的定義。

解決方法
認識了問題,就能夠解決問題:
  在實例化要素中讓編譯器看到模板定義。
  用另外的文件來顯式地實例化類型,這樣鏈接器就能看到該類型。
  使用export關鍵字。

前二種方法通常稱為包含模式,第三種方法則稱為分離模式。

第一種方法意味著在使用模板的轉換文件中不但要包含模板聲明文件,還要包含模板定義文件。在上例中,就是第一個示例,在array.h中用行內(nèi)函數(shù)定義了所有的成員函數(shù)。或者在main.cpp文件中也包含進array.cpp文件。這樣編譯器就能看到模板的聲明和定義,并由此生成array<int, 50>實例。這樣做的缺點是編譯文件會變得很大,顯然要降低編譯和鏈接速度。

第二種方法,通過顯式的模板實例化得到類型。最好將所有的顯式實例化過程安放在另外的文件中。在本例中,可以創(chuàng)建一個新文件templateinstantiations.cpp:
// templateinstantiations.cpp               
#include "array.cpp"

template class array <int, 50>; // 顯式實例化
       
array<int, 50>類型不是在main.cpp中產(chǎn)生,而是在templateinstantiations.cpp中產(chǎn)生。這樣鏈接器就能夠找到它的定義。用這種方法,不會產(chǎn)生巨大的頭文件,加快編譯速度。而且頭文件本身也顯得更加“干凈”和更具有可讀性。但這個方法不能得到惰性實例化的好處,即它將顯式地生成所有的成員函數(shù)。另外還要維護templateinstantiations.cpp文件。

第三種方法是在模板定義中使用export關鍵字,剩下的事就讓編譯器去自行處理了。當我在
Stroustrup的書中讀到export時,感到非常興奮。但很快就發(fā)現(xiàn)VC 6.0不支持它,后來又發(fā)現(xiàn)根本沒有編譯器能夠支持這個關鍵字(第一個支持它的編譯器要在2002年底才問世)。自那以后,我閱讀了不少關于export的文章,了解到它幾乎不能解決用包含模式能夠解決的問題。欲知更多的export關鍵字,建議讀讀Herb Sutter撰寫的文章。

結論
要開發(fā)模板庫,就要知道模板類不是所謂的"原始類型",要用其它的編程思路。本文目的不是要嚇唬那些想進行模板編程的程序員。恰恰相反,是要提醒他們避免犯下開始模板編程時都會出現(xiàn)的錯誤。

 


//////////////////////////////
http://www.cnblogs.com/xgchang/archive/2004/11/12/63139.aspx
甚至是在定義非內(nèi)聯(lián)函數(shù)時,模板的頭文件中也會放置所有的聲明和定義。這似乎違背了通常的頭文件規(guī)則:“不要在分配存儲空間前放置任何東西”,這條規(guī)則是為了防止在連接時的多重定義錯誤。但模板定義很特殊。由template<...>處理的任何東西都意味著編譯器在當時不為它分配存儲空間,它一直出于等待狀態(tài)直到被一個模板實例告知。在編譯器和連接器的某一處,有一機制能去掉模板的多重定義,所以為了容易使用,幾乎總是在頭文件中放置全部的模板聲明和定義。

為什么C++編譯器不能支持對模板的分離式編譯
劉未鵬(pongba) /文

首先,C++標準中提到,一個編譯單元[translation unit]是指一個.cpp文件以及它所include的所有.h文件,.h文件里的代碼將會被擴展到包含它的.cpp文件里,然后編譯器編譯該.cpp文件為一個.obj文件,后者擁有PE[Portable Executable,即windows可執(zhí)行文件]文件格式,并且本身包含的就已經(jīng)是二進制碼,但是,不一定能夠執(zhí)行,因為并不保證其中一定有main函數(shù)。當編譯器將一個工程里的所有.cpp文件以分離的方式編譯完畢后,再由連接器(linker)進行連接成為一個.exe文件。
舉個例子:
//---------------test.h-------------------//
void f();//這里聲明一個函數(shù)f
//---------------test.cpp--------------//
#include”test.h”
void f()
{
…//do something
} //這里實現(xiàn)出test.h中聲明的f函數(shù)
//---------------main.cpp--------------//
#include”test.h”
int main()
{
f(); //調(diào)用f,f具有外部連接類型
}
在這個例子中,test. cpp和main.cpp各被編譯成為不同的.obj文件[姑且命名為test.obj和main.obj],在main.cpp中,調(diào)用了f函數(shù),然而當編譯器編譯main.cpp時,它所僅僅知道的只是main.cpp中所包含的test.h文件中的一個關于void f();的聲明,所以,編譯器將這里的f看作外部連接類型,即認為它的函數(shù)實現(xiàn)代碼在另一個.obj文件中,本例也就是test.obj,也就是說,main.obj中實際沒有關于f函數(shù)的哪怕一行二進制代碼,而這些代碼實際存在于test.cpp所編譯成的test.obj中。在main.obj中對f的調(diào)用只會生成一行call指令,像這樣:
call f [C++中這個名字當然是經(jīng)過mangling[處理]過的]
在編譯時,這個call指令顯然是錯誤的,因為main.obj中并無一行f的實現(xiàn)代碼。那怎么辦呢?這就是連接器的任務,連接器負責在其它的.obj中[本例為test.obj]尋找f的實現(xiàn)代碼,找到以后將call f這個指令的調(diào)用地址換成實際的f的函數(shù)進入點地址。需要注意的是:連接器實際上將工程里的.obj“連接”成了一個.exe文件,而它最關鍵的任務就是上面說的,尋找一個外部連接符號在另一個.obj中的地址,然后替換原來的“虛假”地址。
這個過程如果說的更深入就是:
call f這行指令其實并不是這樣的,它實際上是所謂的stub,也就是一個
jmp 0x23423[這個地址可能是任意的,然而關鍵是這個地址上有一行指令來進行真正的call f動作。也就是說,這個.obj文件里面所有對f的調(diào)用都jmp向同一個地址,在后者那兒才真正”call”f。這樣做的好處就是連接器修改地址時只要對后者的call XXX地址作改動就行了。但是,連接器是如何找到f的實際地址的呢[在本例中這處于test.obj中],因為.obj于.exe的格式都是一樣的,在這樣的文件中有一個符號導入表和符號導出表[import table和export table]其中將所有符號和它們的地址關聯(lián)起來。這樣連接器只要在test.obj的符號導出表中尋找符號f[當然C++對f作了mangling]的地址就行了,然后作一些偏移量處理后[因為是將兩個.obj文件合并,當然地址會有一定的偏移,這個連接器清楚]寫入main.obj中的符號導入表中f所占有的那一項。
這就是大概的過程。其中關鍵就是:
編譯main.cpp時,編譯器不知道f的實現(xiàn),所有當碰到對它的調(diào)用時只是給出一個指示,指示連接器應該為它尋找f的實現(xiàn)體。這也就是說main.obj中沒有關于f的任何一行二進制代碼。
編譯test.cpp時,編譯器找到了f的實現(xiàn)。于是乎f的實現(xiàn)[二進制代碼]出現(xiàn)在test.obj里。
連接時,連接器在test.obj中找到f的實現(xiàn)代碼[二進制]的地址[通過符號導出表]。然后將main.obj中懸而未決的call XXX地址改成f實際的地址。
完成。

然而,對于模板,你知道,模板函數(shù)的代碼其實并不能直接編譯成二進制代碼,其中要有一個“具現(xiàn)化”的過程。舉個例子:
//----------main.cpp------//
template<class T>
void f(T t)
{}
int main()
{
…//do something
f(10); //call f<int> 編譯器在這里決定給f一個f<int>的具現(xiàn)體
…//do other thing
}
也就是說,如果你在main.cpp文件中沒有調(diào)用過f,f也就得不到具現(xiàn),從而main.obj中也就沒有關于f的任意一行二進制代碼!!如果你這樣調(diào)用了:
f(10); //f<int>得以具現(xiàn)化出來
f(10.0); //f<double>得以具現(xiàn)化出來
這樣main.obj中也就有了f<int>,f<double>兩個函數(shù)的二進制代碼段。以此類推。
然而具現(xiàn)化要求編譯器知道模板的定義,不是嗎?
看下面的例子:[將模板和它的實現(xiàn)分離]
//-------------test.h----------------//
template<class T>
class A
{
public:
void f(); //這里只是個聲明
};
//---------------test.cpp-------------//
#include”test.h”
template<class T>
void A<T>::f() //模板的實現(xiàn),但注意:不是具現(xiàn)
{
…//do something
}
//---------------main.cpp---------------//
#include”test.h”
int main()
{
A<int> a;
a. f(); //編譯器在這里并不知道A<int>::f的定義,因為它不在test.h里面
//于是編譯器只好寄希望于連接器,希望它能夠在其他.obj里面找到
//A<int>::f的實現(xiàn)體,在本例中就是test.obj,然而,后者中真有A<int>::f的
//二進制代碼嗎?NO!!!因為C++標準明確表示,當一個模板不被用到的時
//侯它就不該被具現(xiàn)出來,test.cpp中用到了A<int>::f了嗎?沒有!!所以實
//際上test.cpp編譯出來的test.obj文件中關于A::f的一行二進制代碼也沒有
//于是連接器就傻眼了,只好給出一個連接錯誤
//但是,如果在test.cpp中寫一個函數(shù),其中調(diào)用A<int>::f,則編譯器會將其//具現(xiàn)出來,因為在這個點上[test.cpp中],編譯器知道模板的定義,所以能//夠具現(xiàn)化,于是,test.obj的符號導出表中就有了A<int>::f這個符號的地
//址,于是連接器就能夠完成任務。
}

關鍵是:在分離式編譯的環(huán)境下,編譯器編譯某一個.cpp文件時并不知道另一個.cpp文件的存在,也不會去查找[當遇到未決符號時它會寄希望于連接器]。這種模式在沒有模板的情況下運行良好,但遇到模板時就傻眼了,因為模板僅在需要的時候才會具現(xiàn)化出來,所以,當編譯器只看到模板的聲明時,它不能具現(xiàn)化該模板,只能創(chuàng)建一個具有外部連接的符號并期待連接器能夠將符號的地址決議出來。然而當實現(xiàn)該模板的.cpp文件中沒有用到模板的具現(xiàn)體時,編譯器懶得去具現(xiàn),所以,整個工程的.obj中就找不到一行模板具現(xiàn)體的二進制代碼,于是連接器也黔

 

 

/////////////////////////////////
http://dev.csdn.net/develop/article/19/19587.shtm
 C++模板代碼的組織方式 ——包含模式(Inclusion Model)     選擇自 sam1111 的 Blog 
關鍵字   Template Inclusion Model
出處   C++ Template: The Complete Guide


說明:本文譯自《C++ Template: The Complete Guide》一書的第6章中的部分內(nèi)容。最近看到C++論壇上常有關于模板的包含模式的帖子,聯(lián)想到自己初學模板時,也為類似的問題困惑過,因此翻譯此文,希望對初學者有所幫助。

模板代碼有幾種不同的組織方式,本文介紹其中最流行的一種方式:包含模式。

鏈接錯誤

大多數(shù)C/C++程序員向下面這樣組織他們的非模板代碼:

         ·類和其他類型全部放在頭文件中,這些頭文件具有.hpp(或者.H, .h, .hh, .hxx)擴展名。

         ·對于全局變量和(非內(nèi)聯(lián))函數(shù),只有聲明放在頭文件中,而定義放在點C文件中,這些文件具有.cpp(或者.C, .c, .cc, .cxx)擴展名。
 

這種組織方式工作的很好:它使得在編程時可以方便地訪問所需的類型定義,并且避免了來自鏈接器的“變量或函數(shù)重復定義”的錯誤。
 

由于以上組織方式約定的影響,模板編程新手往往會犯一個同樣的錯誤。下面這一小段程序反映了這種錯誤。就像對待“普通代碼”那樣,我們在頭文件中定義模板:
 

// basics/myfirst.hpp 

#ifndef MYFIRST_HPP
#define MYFIRST_HPP 

// declaration of template

template <typename T>

void print_typeof (T const&);

#endif // MYFIRST_HPP

 

print_typeof()聲明了一個簡單的輔助函數(shù)用來打印一些類型信息。函數(shù)的定義放在點C文件中:

// basics/myfirst.cpp

#include <iostream>

#include <typeinfo>

#include "myfirst.hpp"
 

// implementation/definition of template

template <typename T>
void print_typeof (T const& x)
{

    std::cout << typeid(x).name() << std::endl;

}

 

這個例子使用typeid操作符來打印一個字符串,這個字符串描述了傳入的參數(shù)的類型信息。

最后,我們在另外一個點C文件中使用我們的模板,在這個文件中模板聲明被#include:

// basics/myfirstmain.cpp 

#include "myfirst.hpp" 

// use of the template

int main()
{

    double ice = 3.0;
    print_typeof(ice);  // call function template for type double

}

 
大部分C++編譯器(Compiler)很可能會接受這個程序,沒有任何問題,但是鏈接器(Linker)大概會報告一個錯誤,指出缺少函數(shù)print_typeof()的定義。

這個錯誤的原因在于,模板函數(shù)print_typeof()的定義還沒有被具現(xiàn)化(instantiate)。為了具現(xiàn)化一個模板,編譯器必須知道哪一個定義應該被具現(xiàn)化,以及使用什么樣的模板參數(shù)來具現(xiàn)化。不幸的是,在前面的例子中,這兩組信息存在于分開編譯的不同文件中。因此,當我們的編譯器看到對print_typeof()的調(diào)用,但是沒有看到此函數(shù)為double類型具現(xiàn)化的定義時,它只是假設這樣的定義在別處提供,并且創(chuàng)建一個那個定義的引用(鏈接器使用此引用解析)。另一方面,當編譯器處理myfirst.cpp時,該文件并沒有任何指示表明它必須為它所包含的特殊參數(shù)具現(xiàn)化模板定義。

頭文件中的模板

解決上面這個問題的通用解法是,采用與我們使用宏或者內(nèi)聯(lián)函數(shù)相同的方法:我們將模板的定義包含進聲明模板的頭文件中。對于我們的例子,我們可以通過將#include "myfirst.cpp"添加到myfirst.hpp文件尾部,或者在每一個使用我們的模板的點C文件中包含myfirst.cpp文件,來達到目的。當然,還有第三種方法,就是刪掉myfirst.cpp文件,并重寫myfirst.hpp文件,使它包含所有的模板聲明與定義:


// basics/myfirst2.hpp

#ifndef MYFIRST_HPP
#define MYFIRST_HPP 

#include <iostream>
#include <typeinfo>
 

// declaration of template
template <typename T>
void print_typeof (T const&); 

// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
{

    std::cout << typeid(x).name() << std::endl;

}

#endif // MYFIRST_HPP

 

這種組織模板代碼的方式就稱作包含模式。經(jīng)過這樣的調(diào)整,你會發(fā)現(xiàn)我們的程序已經(jīng)能夠正確編譯、鏈接、執(zhí)行了。

從這個方法中我們可以得到一些觀察結果。最值得注意的一點是,這個方法在相當程度上增加了包含myfirst.hpp的開銷。在這個例子中,這種開銷并不是由模板定義自身的尺寸引起的,而是由這樣一個事實引起的,即我們必須包含我們的模板用到的頭文件,在這個例子中是<iostream>和<typeinfo>。你會發(fā)現(xiàn)這最終導致了成千上萬行的代碼,因為諸如<iostream>這樣的頭文件也包含了和我們類似的模板定義。

這在實踐中確實是一個問題,因為它增加了編譯器在編譯一個實際程序時所需的時間。我們因此會在以后的章節(jié)中驗證其他一些可能的方法來解決這個問題。但無論如何,現(xiàn)實世界中的程序花一小時來編譯鏈接已經(jīng)是快的了(我們曾經(jīng)遇到過花費數(shù)天時間來從源碼編譯的程序)。

拋開編譯時間不談,我們強烈建議如果可能盡量按照包含模式組織模板代碼。

另一個觀察結果是,非內(nèi)聯(lián)模板函數(shù)與內(nèi)聯(lián)函數(shù)和宏的最重要的不同在于:它并不會在調(diào)用端展開。相反,當模板函數(shù)被具現(xiàn)化時,會產(chǎn)生此函數(shù)的一個新的拷貝。由于這是一個自動的過程,編譯器也許會在不同的文件中產(chǎn)生兩個相同的拷貝,從而引起鏈接器報告一個錯誤。理論上,我們并不關心這一點:這是編譯器設計者應當關心的事情。實際上,大多數(shù)時候一切都運轉正常,我們根本就不用處理這種狀況。然而,對于那些需要創(chuàng)建自己的庫的大型項目,這個問題偶爾會顯現(xiàn)出來。
 

最后,需要指出的是,在我們的例子中,應用于普通模板函數(shù)的方法同樣適用于模板類的成員函數(shù)和靜態(tài)數(shù)據(jù)成員,以及模板成員函數(shù)。

posted on 2009-07-09 22:33 yang-chunlei 閱讀(205) 評論(0)  編輯 收藏 引用
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久成人免费| 欧美日韩国产在线| 中文欧美日韩| 老色批av在线精品| 国产综合亚洲精品一区二| 中文欧美日韩| 最新亚洲一区| 亚洲伊人久久综合| 欧美日韩亚洲三区| 一区二区免费在线视频| 亚洲三级免费| 国产日韩精品在线观看| 久久国产精品99精品国产| 一区二区三区欧美激情| 亚洲伦理中文字幕| 亚洲免费高清视频| 亚洲午夜av在线| 亚洲国产日韩欧美综合久久| 亚洲一区影院| 亚洲天堂第二页| 国产精品毛片大码女人| 香蕉乱码成人久久天堂爱免费 | 亚洲激情电影在线| 亚洲欧美综合国产精品一区| 免费在线观看精品| 欧美一区二区在线播放| 欧美精品情趣视频| 久久精品天堂| 久久综合久久综合这里只有精品| 久久大逼视频| 亚洲一区二区在线| 欧美一区二视频在线免费观看| 亚洲精品一二| 欧美1区2区视频| 欧美www视频| 韩国av一区二区三区| 午夜免费日韩视频| 亚洲永久免费| 欧美午夜片在线免费观看| 亚洲乱码国产乱码精品精98午夜| 黄色成人在线| 亚洲一区亚洲| 久久天天躁狠狠躁夜夜av| 国内伊人久久久久久网站视频 | 在线观看成人小视频| 久久久在线视频| 国产一区二区三区四区hd| 欧美一区二区精品| 老司机午夜精品视频在线观看| 韩国av一区二区三区四区| 玖玖视频精品| 一个色综合导航| 老司机一区二区| 中日韩午夜理伦电影免费| 国产日韩av在线播放| 欧美成人黑人xx视频免费观看| 亚洲欧美日韩天堂一区二区| 午夜视频在线观看一区| 亚洲性视频网址| 亚洲乱码国产乱码精品精天堂| 欧美啪啪成人vr| 国产乱码精品一区二区三区忘忧草| 亚洲免费小视频| 欧美综合国产| 99re热这里只有精品视频| 国产女主播一区二区| 欧美精品二区三区四区免费看视频| 亚洲一区二区三区精品在线观看 | 国产精品视频一二| 欧美.www| 久久综合久久综合这里只有精品| 宅男噜噜噜66国产日韩在线观看| 欧美刺激性大交免费视频| 久久精品国产第一区二区三区最新章节 | 久久精品人人爽| 欧美aaa级| 日韩视频不卡中文| 亚洲欧洲日产国码二区| 国产一区白浆| 国产一区二区高清| 欧美午夜不卡视频| 国产精品乱码| 国产日韩欧美一区在线| 国产欧美日韩另类一区| 国产精品捆绑调教| 国产精品美女视频网站| 国产欧美日韩综合| 国产日韩欧美综合| 国产一区二区三区高清在线观看| 好看不卡的中文字幕| 亚洲国内精品在线| 99热免费精品在线观看| 亚洲欧美国产精品va在线观看| 久久国产色av| 91久久线看在观草草青青| 一本久道久久综合狠狠爱| 久久精品国产欧美亚洲人人爽| 亚洲天堂黄色| 午夜老司机精品| 久久久久久9| 欧美成人一区二免费视频软件| 亚洲二区在线观看| 亚洲一区三区视频在线观看 | 91久久精品国产91性色| 亚洲视频一区| 欧美精品久久久久久久久老牛影院| 欧美午夜视频在线观看| 国产婷婷色综合av蜜臀av | 亚洲一区精品在线| 欧美成黄导航| 红桃视频一区| 欧美一区免费视频| 亚洲精品偷拍| 亚洲美女黄网| 久久综合国产精品| 欧美日韩性视频在线| 亚洲经典在线| 免费视频亚洲| 久久综合网色—综合色88| 韩国精品在线观看| 欧美一区二区三区的| 亚洲久久一区| 国产精品白丝黑袜喷水久久久| 亚洲精品在线免费| 免费欧美日韩| 久久免费偷拍视频| 亚洲精品国产精品国自产在线 | 欧美成人免费大片| 久久夜色精品| 亚洲免费人成在线视频观看| 午夜视频一区二区| 亚洲国产精品高清久久久| 亚洲电影观看| 久久精品一区二区三区不卡| 欧美一级黄色录像| 欧美91精品| 国产精品激情偷乱一区二区∴| 欧美在线看片| 欧美巨乳在线| 老**午夜毛片一区二区三区| 欧美日韩国产综合一区二区| 久久精品导航| 国产精品高潮粉嫩av| 麻豆av一区二区三区久久| 欧美巨乳波霸| 欧美/亚洲一区| 国产一区二区三区在线免费观看| 亚洲电影免费观看高清完整版| 欧美揉bbbbb揉bbbbb| 久久久久久久久一区二区| 久久久亚洲高清| 亚洲图片欧美日产| 欧美激情精品久久久六区热门| 国产伦精品一区二区三区| 欧美激情视频免费观看| 亚洲承认在线| 免费观看一区| 国产精品狠色婷| 久久精品视频免费播放| 欧美日韩hd| 亚洲人午夜精品免费| 精品1区2区3区4区| 久久久久久久久久久一区| 日韩一区二区精品视频| 久久精品一区二区三区四区| 国产有码一区二区| 久久九九精品| 另类av一区二区| 一区二区三区在线视频观看| 久久精品国产欧美激情| 久久精品日韩欧美| 好吊一区二区三区| 欧美日韩不卡| 亚洲激情视频网| 99re6这里只有精品| 国产精品福利在线观看| 欧美亚洲在线视频| 毛片av中文字幕一区二区| 在线不卡中文字幕| 欧美精品久久久久久久久久| 美女诱惑一区| 欧美精品在线一区| 亚洲国产精品一区制服丝袜| 久久久一区二区| 国产亚洲欧美aaaa| 久久成人资源| 99精品欧美| 一区二区三区四区精品| 欧美日本不卡高清| 亚洲午夜小视频| 欧美在线一二三四区| 激情六月婷婷久久| 午夜一区二区三视频在线观看| 欧美国产日韩亚洲一区| 亚洲综合欧美日韩| 亚洲国产裸拍裸体视频在线观看乱了中文| 欧美1区2区3区| 亚洲欧美日韩天堂| 这里是久久伊人| 欧美激情精品久久久久久蜜臀|