• <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>

            旅途

            如果想飛得高,就該把地平線忘掉

            C++/CLR泛型與C++模板之間的對比

            Visual Studio 2005把泛型編程的類型參數(shù)模型引入了微軟.NET框架組件。C++/CLI支持兩種類型參數(shù)機(jī)制--通用語言運(yùn)行時(CLR)泛型和C++模板。本文將介紹兩者之間的一些區(qū)別--特別是參數(shù)列表和類型約束模型之間的區(qū)別。

              參數(shù)列表又回來了

              參數(shù)列表與函數(shù)的信號(signature)類似:它標(biāo)明了參數(shù)的數(shù)量和每個參數(shù)的類型,并把給每個參數(shù)關(guān)聯(lián)一個唯一的標(biāo)識符,這樣在模板定義的內(nèi)部,每個參數(shù)就可以被唯一地引用。

              參數(shù)在模板或泛型的定義中起占位符(placeholder)的作用。用戶通過提供綁定到參數(shù)的實(shí)際值來建立對象實(shí)例。參數(shù)化類型的實(shí)例化并非簡單的文本替代(宏擴(kuò)展機(jī)制就是使用文本替代的)。相反地,它把實(shí)際的用戶值綁定到定義中的相關(guān)的形式參數(shù)上。

              在泛型中,每個參數(shù)都表現(xiàn)為Object類型或衍生自O(shè)bject的類型。在本文后面你可以看到,這約束了你可能執(zhí)行的操作類型或通過類型參數(shù)聲明的對象。你可以通過提供更加明確的約束來調(diào)整這些約束關(guān)系。這些明確的約束引用那些衍生出實(shí)際類型參數(shù)的基類或接口集合。

              模板除了支持類型參數(shù)之外,還支持表達(dá)式和模板參數(shù)。此外,模板還支持默認(rèn)的參數(shù)值。這些都是按照位置而不是名稱來分解的。在兩種機(jī)制之下,類型參數(shù)都是與類或類型名稱關(guān)鍵字一起引入的。

              參數(shù)列表的額外的模板功能

              模板作為類型參數(shù)的補(bǔ)充,允許兩種類型的參數(shù):非類型(non-type)參數(shù)和模板參數(shù)。我們將分別簡短地介紹一下。

               非類型參數(shù)受常數(shù)表達(dá)式的約束。我們應(yīng)該立即想到它是數(shù)值型或字符串常量。例如,如果選擇提供固定大小的堆棧,你就可能同時指定一個非類型的大小參數(shù)和 元素類型參數(shù),這樣就可以同時按照元素類別和大小來劃分堆棧實(shí)例的類別。例如,你可以在代碼1中看到帶有非類型參數(shù)的固定大小的堆棧。

              代碼1:帶有非類型固定大小的堆棧

            template <class elemType, int size>
            public ref class tStack
            {
             array<elemType> ^m_stack;
             int top;

             public:
              tStack() : top( 0 )
              { m_stack = gcnew array<elemType>( size ); }
            };

              此外,如果模板類設(shè)計者可以為每個參數(shù)指定默認(rèn)值,使用起來就可能方便多了。例如,把緩沖區(qū)的默認(rèn)大小設(shè)置為1KB就是很好的。在模板機(jī)制下,可以給參數(shù)提供默認(rèn)值,如下所示:

            // 帶有默認(rèn)值的模板聲明
            template <class elemType, int size = 1024>
            public ref class FixedSizeStack {};

              用戶可以通過提供明確的第二個值來重載默認(rèn)大小值:

            // 最多128個字符串實(shí)例的堆棧
            FixedSizeState<String^, 128> ^tbs = gcnew FixedSizeStack<String^, 128>;

              否則,由于沒有提供第二個參數(shù),它使用了相關(guān)的默認(rèn)值,如下所示:

            // 最多1024個字符串實(shí)例的堆棧
            FixedSizeStack<String^> ^tbs = gcnew FixedSizeStack<String^>;

              使用默認(rèn)的參數(shù)值是標(biāo)準(zhǔn)模板庫(STL)的一個基本的設(shè)計特征。例如,下面的聲明就來自ISO-C++標(biāo)準(zhǔn):

            // ISO-C++名字空間std中的默認(rèn)類型參數(shù)值示例
            {
             template <class T, class Container = deque<T> >
             class queue;

             template <class T, class Allocator = allocator<T> >
             class vector;
             // ...
            }

              你可以提供默認(rèn)的元素類型,如下所示:

            // 帶有默認(rèn)的元素類型的模板聲明
            template <class elemType=String^, int size=1024>
            public ref class tStack {};

              從設(shè)計的角度來說很難證明它的正確性,因?yàn)橐话銇碚f容器不會集中在在單個默認(rèn)類型上。

               指針也可以作為非類型參數(shù),因?yàn)閷ο蠡蚝瘮?shù)的地址在編譯時就已知了,因此是一個常量表達(dá)式。例如,你可能希望為堆棧類提供第三個參數(shù),這個參數(shù)指明遇到 特定條件的時候使用的回調(diào)處理程序。明智地使用typedef可以大幅度簡化那些表面上看起來很復(fù)雜的聲明,如下所示:

            typedef void (*handler)( ... array<Object^>^ );
            template <class elemType, int size, handler cback >
            public ref class tStack {};

              當(dāng)然,你可以為處理程序提供默認(rèn)值--在這個例子中,是一個已有的方法的地址。例如,下面的緩沖區(qū)聲明就提供了大小和處理程序:

            void defaultHandler( ... array<Object^>^ ){ ... }

            template < class elemType,
            int size = 1024,
            handler cback = &defaultHandler >
            public ref class tStack {};

              由于默認(rèn)值的位置次序優(yōu)先于命名次序,因此如果不提供明確的大小值(即使這個大小與默認(rèn)值是重復(fù)的),就無法提供重載的處理程序的。下面就是可能用到的修改堆棧的方法:

            void demonstration()
            {
             // 默認(rèn)的大小和處理程序
             tStack<String^> ^ts1 = nullptr;
             // 默認(rèn)的處理程序
             tStack<String^, 128> ^ts2 = gcnew tStack<String^, 128>;
             // 重載所有的三個參數(shù)
             tStack<String^, 512, &yourHandler> ^ts3;
            }

              模板支持的第二種額外的參數(shù)就是template模板參數(shù)--也就是這個模板參數(shù)本身表現(xiàn)為一個模板。例如:

            // template模板參數(shù)
            template <template <class T> class arena, class arenaType>
            class Editor {
            arena<arenaType> m_arena;
            // ...
            };

               Editor模板類列出了兩個模板參數(shù)arena和arenaType。ArenaType是一個模板類型參數(shù);你可以傳遞整型、字符串型、自定義類型 等等。Arena是一個template模板參數(shù)。帶有單個模板類型參數(shù)的任何模板類都可以綁定到arena。m_arena是一個綁定到 arenaType模板類型參數(shù)的模板類實(shí)例。例如:

            // 模板緩沖區(qū)類
            template <class elemType>
            public ref class tBuffer {};

            void f()
            {
             Editor<tBuffer,String^> ^textEditor;
             Editor<tBuffer,char> ^blitEditor;
             // ...
            }

            類型參數(shù)約束

              如果你把參數(shù)化類型簡單地作為存儲和檢索元素的容器,那么你可以略過這一部分了。當(dāng)你需要調(diào)用某個類型參數(shù)(例如在比較兩個對象,查看它們相等或者其中一個小于另一個的時候,或者通過類型參數(shù)調(diào)用方法名稱或嵌套類型的時候)上的操作的時候,才會考慮約束的問題。例如:

            template <class T>
            ref class Demonstration {
             int method() {
              typename T::A *aObj;
              // ...
             }
            };

              這段代碼成功地聲明了aObj,它同時還約束了能夠成功地綁定到你的類模板的類型參數(shù)。例如,如果你編寫下面的代碼,aObj的聲明就是非法的(在這種特定的情況下),編譯器會報錯誤信息:

            int demoMethod()
            {
             Demonstration<int> ^demi = gcnew Demonstration<int>( 1024 );
             return dm->method();
            }

               當(dāng)然,其特定的約束是,這個類型參數(shù)必須包含一個叫做A的類型的嵌套聲明。如果它的名字叫做B、C或Z都沒有關(guān)系。更普通的約束是類型參數(shù)必須表示一個 類,否則就不允許使用T::范圍操作符。我使用int類型參數(shù)同時違反了這兩條約束。例如,Visual C++編譯器會生成下面的錯誤信息:

            error C2825: 'T': must be a class or namespace when followed by '::'

               C++模板機(jī)制受到的一條批評意見是:缺乏用于描述這種類型約束的形式語法(請注意,在參數(shù)化類型的原始設(shè)計圖紙中,Bjarne Stroustrup論述了曾經(jīng)考慮過提供顯式約束語法,但是他對這種語法不太滿意,并選擇了在那個時候不提供這種機(jī)制)。也就是說,在一般情況下,用戶 在閱讀源代碼或相關(guān)的文檔,或者編譯自己的代碼并閱讀隨后的編譯器錯誤消息的時候,才能意識到模板有隱含約束。

              如果你必須提供一個與模板不匹配的類型參數(shù)該怎么辦呢?一方面,我們能做的事情很少。你編寫的任何類都有一定的假設(shè),這些假設(shè)表現(xiàn)為某些使用方面的約束。很難設(shè)計出適合每種情況的類;設(shè)計出適合每種情況和每種可能的類型參數(shù)的模板類更加困難。

              另一方面,存在大量的模板特性為用戶提供了"迂回"空間。例如,類模板成員函數(shù)不會綁定到類型參數(shù),直到在代碼中使用該函數(shù)為止(這個時候才綁定)。因此,如果你使用模板類的時候,沒有使用那些使類型參數(shù)失效的方法,就不會遇到問題。

               如果這樣也不可行,那么還可以提供該方法的一個專門的版本,讓它與你的類型參數(shù)關(guān)聯(lián)。在這種情況下,你需要提供Demonstration< int>::方法的一個專用的實(shí)例,或者,更為普遍的情況是,在提供整數(shù)類型參數(shù)的時候,提供整個模板類的專門的實(shí)現(xiàn)方式。

              一般來說,當(dāng)你提到參數(shù)化類型可以支持多種類型的時候,你一般談到的是參數(shù)化的被動使用--也就是說,主要是類型的存儲和檢索,而不是積極地操作(處理)它。

               作為模板的設(shè)計人員,你必須知道自己的實(shí)現(xiàn)對類型參數(shù)的隱含約束條件,并且努力去確保這些條件不是多余的。例如,要求類型參數(shù)提供等于和小于操作是合理 的;但是要求它支持小于或等于或XOR位運(yùn)算符就不太合理了。你可以通過把這些操作分解到不同的接口中,或者要求額外的、表示函數(shù)、委托或函數(shù)對象的參數(shù) 來放松對操作符的依賴性。例如,代碼2顯示了一個本地C++程序員使用內(nèi)建的等于操作符實(shí)現(xiàn)的搜索方法。

              代碼2:不利于模板的搜索實(shí)現(xiàn)

            template <class elemType, int size=1024>
            ref class Container
            {
             array<elemType> ^m_buf;
             int next;

             public:
              bool search( elemType et )
              {
               for each ( elemType e in m_buf )
                if ( et == e )
                 return true;
                return false;
              }

              Container()
              {
               m_buf = gcnew array<elemType>(size);
               next = 0;
              }

              void add( elemType et )
              {
               if ( next >= size )
                throw gcnew Exception;
                m_buf[ next++ ] = et;
              }

              elemType get( int ix )
              {
               if ( ix < next )
                return m_buf[ ix ];
               throw gcnew Exception;
              }
              // ...
             };

               在這個搜索函數(shù)中沒有任何錯誤。但是,它不太利于使用模板,因?yàn)轭愋蛥?shù)與等于操作符緊密耦合了。更為靈活的方案是提供第二個搜索方法,允許用戶傳遞一 個對象來進(jìn)行比較操作。你可以使用函數(shù)成員模板來實(shí)現(xiàn)這個功能。函數(shù)成員模板提供了一個額外的類型參數(shù)。請看一看代碼3。

              代碼3:使用模板

            template <class elemType, int size=1024>
            ref class Container
            {
             // 其它的都相同 ...
             // 這是一個函數(shù)成員模板...
             // 它可以同時引用包含的類參數(shù)和自有參數(shù)...

             template <class Comparer>
             bool search( elemType et, Comparer comp )
             {
              for each ( elemType e in m_buf )
               if ( comp( et, e ) )
                return true;
             
               return false;
             }
             // ...
            };

              現(xiàn)在用戶可以選擇使用哪一個方法來搜索內(nèi)容了:緊密耦合的等于操作符搜索效率較高,但是不適合于所有類型;較靈活的成員模板搜索要求傳遞用于比較的類型。

              哪些對象適用這種比較目的?函數(shù)對象就是普通的用于這種目的的C++設(shè)計模式。例如,下面就是一個比較兩個字符串是否相等的函數(shù)對象:

            class EqualGuy {
             public:
              bool operator()( String^ s1, String^ s2 )
              {
               return s1->CompareTo( s2 ) == 0;
              }
            };

              代碼4中的代碼顯示了你如何調(diào)用這兩個版本的搜索成員函數(shù)模板和傳統(tǒng)的版本。

              代碼4:兩個搜索函數(shù)

            int main()
            {
             Container<String^> ^sxc = gcnew Container<String^>;
             sxc->add( "Pooh" );
             sxc->add( "Piglet" );

             // 成員模板搜索 ...
             if ( sxc->search( "Pooh", EqualGuy() ) )
              Console::WriteLine( "found" );
             else Console::WriteLine( "not found" );

              // 傳統(tǒng)的等于搜索 ...
              if ( sxc->search( "Pooh" ) )
               Console::WriteLine( "found" );
              else Console::WriteLine( "not found" );
            }

              一旦有了模板的概念,你就會發(fā)現(xiàn)使用模板幾乎沒有什么事情不是實(shí)現(xiàn)。至少感覺是這樣的。
            泛型約束

              與模板不同,泛型定義支持形式約束語法,這些語法用于描述可以合法地綁定的類型參數(shù)。在我詳細(xì)介紹約束功能之前,我們簡短地考慮一下為什么泛型選擇了提供約束功能,而模板選擇了不提供這個功能。我相信,最主要的原因是兩種機(jī)制的綁定時間之間差異。

              模板在編譯的過程中綁定,因此無效的類型會讓程序停止編譯。用戶必須立即解決這個問題或者把它重新處理成非模板編程方案。執(zhí)行程序的完整性不存在風(fēng)險。

              另一方面,泛型在運(yùn)行時綁定,在這個時候才發(fā)現(xiàn)用戶指定的類型無效就已經(jīng)太遲了。因此通用語言結(jié)構(gòu)(CLI)需要一些靜態(tài)(也就是編譯時)機(jī)制來確保在運(yùn)行時只會綁定有效的類型。與泛型相關(guān)的約束列表是編譯時過濾器,也就是說,如果違反的時候,會阻止程序的建立。

               我們來看一個例子。圖5顯示了用泛型實(shí)現(xiàn)的容器類。它的搜索方法假設(shè)類型參數(shù)衍生自Icomparable,因此它實(shí)現(xiàn)了該接口的CompareTo方 法的一個實(shí)例。請注意,容器的大小是在構(gòu)造函數(shù)中由用戶提供的,而不是作為第二個、非類型參數(shù)提供的。你應(yīng)該記得泛型不支持非類型參數(shù)的。

              代碼5:作為泛型實(shí)現(xiàn)的容器

            generic <class elemType>
            public ref class Container
            {
             array<elemType> ^m_buf;
             int next;
             int size;
             
            public:
             bool search( elemType et )
             {
              for each ( elemType e in m_buf )
               if ( et->CompareTo( e ))
                return true;
               return false;
             }

             Container( int sz )
             {
              m_buf = gcnew array<elemType>(size = sz);
              next = 0;
             }

             // add() 和 get() 是相同的 ...

            };

              該泛型類的實(shí)現(xiàn)在編譯的時候失敗了,遇到了如下所示的致命的編譯診斷信息:

            error C2039: 'CompareTo' : is not a member of 'System::Object'

               你也許有點(diǎn)糊涂了,這是怎么回事?沒有人認(rèn)為它是System::Object的成員啊。但是,在這種情況下你就錯了。在默認(rèn)情況下,泛型參數(shù)執(zhí)行最嚴(yán) 格的可能的約束:它把自己的所有類型約束為Object類型。這個約束條件是對的,因?yàn)橹辉试SCLI類型綁定到泛型上,當(dāng)然,所有的CLI類型都多多少少 地衍生自O(shè)bject。因此在默認(rèn)情況下,作為泛型的作者,你的操作非常安全,但是可以使用的操作也是有限的。

              你可能會想,好吧,我減小靈活性,避免編譯器錯誤,用等于操作符代替CompareTo方法,但是它卻引起了更嚴(yán)重的錯誤:

            error C2676: binary '==' : 'elemType' does not define this operator
            or a conversion to a type acceptable to the predefined operator

               同樣,發(fā)生的情況是,每個類型參數(shù)開始的時候都被Object的四個公共的方法包圍著:ToString、GetType、GetHashCode和 Equals。其效果是,這種在單獨(dú)的類型參數(shù)上列出約束條件的工作表現(xiàn)了對初始的強(qiáng)硬約束條件的逐步放松。換句話說,作為泛型的作者,你的任務(wù)是按照泛 型約束列表的約定,采用可以驗(yàn)證的方式來擴(kuò)展那些允許的操作。我們來看看如何實(shí)現(xiàn)這樣的事務(wù)。

              我們用約束子句來引用約束列表,使用非 保留字"where"實(shí)現(xiàn)。它被放置在參數(shù)列表和類型聲明之間。實(shí)際的約束包含一個或多個接口類型和/或一個類類型的名稱。這些約束顯示了參數(shù)類型希望實(shí) 現(xiàn)的或者衍生出類型參數(shù)的基類。每種類型的公共操作集合都被添加到可用的操作中,供類型參數(shù)使用。因此,為了讓你的elemType參數(shù)調(diào)用 CompareTo,你必須添加與Icomparable接口關(guān)聯(lián)的約束子句,如下所示:

            generic <class elemType>
            where elemType : IComparable
            public ref class Container
            {
             // 類的主體沒有改變 ...
            };

               這個約束子句擴(kuò)展了允許elemType實(shí)例調(diào)用的操作集合,它是隱含的Object約束和顯式的Icomparable約束的公共操作的結(jié)合體。該泛 型定義現(xiàn)在可以編譯和使用了。當(dāng)你指定一個實(shí)際的類型參數(shù)的時候(如下面的代碼所示),編譯器將驗(yàn)證實(shí)際的類型參數(shù)是否與將要綁定的類型參數(shù)的約束相匹 配:

            int main()
            {
             // 正確的:String和int實(shí)現(xiàn)了IComparable
             Container<String^> ^sc;
             Container<int> ^ic;

             //錯誤的:StringBuilder沒有實(shí)現(xiàn)IComparable
             Container<StringBuilder^> ^sbc;
            }

              編譯器會提示某些違反了規(guī)則的信息,例如sbc的定義。但是泛型的實(shí)際的綁定和構(gòu)造已經(jīng)由運(yùn)行時完成了。

              接著,它會同時驗(yàn)證泛型在定義點(diǎn)(編譯器處理你的實(shí)現(xiàn)的時候)和構(gòu)造點(diǎn)(編譯器根據(jù)相關(guān)的約束條件檢查類型參數(shù)的時候)是否違反了約束。無論在那個點(diǎn)失敗都會出現(xiàn)編譯時錯誤。

              約束子句可以每個類型參數(shù)包含一個條目。條目的次序不一定跟參數(shù)列表的次序相同。某個參數(shù)的多個約束需要使用逗號分開。約束在與每個參數(shù)相關(guān)的列表中必須唯一,但是可以出現(xiàn)在多個約束列表中。例如:

            generic <class T1, class T2, class T3>
            where T1 : IComparable, ICloneable, Image
            where T2 : IComparable, ICloneable, Image
            where T3 : ISerializable, CompositeImage
            public ref class Compositor
            {
             // ...
            };

               在上面的例子中,出現(xiàn)了三個約束子句,同時指定了接口類型和一個類類型(在每個列表的末尾)。這些約束是有額外的意義的,即類型參數(shù)必須符合所有列出的 約束,而不是符合它的某個子集。我的同事Jon Wray指出,由于你是作為泛型的作者來擴(kuò)展操作集合的,因此如果放松了約束條件,那么該泛型的用戶在選擇類型參數(shù)的時候就得增加更多的約束。

              T1、T2和T3子句可以按照其它的次序放置。但是,不允許跨越兩個或多個子句指定某個類型參數(shù)的約束列表。例如,下面的代碼就會出現(xiàn)違反語法錯誤:

            generic <class T1, class T2, class T3>
            // 錯誤的:同一個參數(shù)不允許有兩個條目
            where T1 : IComparable, ICloneable
            where T1 : Image
            public ref class Compositor
            {
             // ...
            };

               類約束類型必須是未密封的(unsealed)參考類(數(shù)值類和密封類都是不允許的,因?yàn)樗鼈儾辉试S繼承)。有四個System名字空間類是禁止出現(xiàn)在 約束子句中的,它們分別是:System::Array、System::Delegate、 System::Enum和System::ValueType。由于CLI只支持單繼承(single inheritance),約束子句只支持一個類類型的包含。約束類型至少要像泛型或函數(shù)那樣容易訪問。例如,你不能聲明一個公共泛型并列出一個或多個內(nèi) 部可視的約束。

              任何類型參數(shù)都可以綁定到一個約束類型。下面是一個簡單的例子:

            generic <class T1, class T2>
            where T1 : IComparable<T1>
            where T2 : IComparable<T2>
            public ref class Compositor
            {
             // ...
            };

              約束是不能繼承的。例如,如果我從Compositor繼承得到下面的類,Compositor的T1和T2上的Icomparable約束不會應(yīng)用在 BlackWhite_Compositor類的同名參數(shù)上:

            generic <class T1, class T2>
            public ref class BlackWhite_Compositor : Compositor
            {
             // ...
            };

              當(dāng)這些參數(shù)與基類一起使用的時候,這就有幾分設(shè)計方面的便利了。為了保證Compositor的完整性,BlackWhite_Compositor必須把Compositor約束傳播給那些傳遞到Compositor子對象的所有參數(shù)。例如,正確的聲明如下所示:

            generic <class T1, class T2>
            where T1 : IComparable<T1>
            where T2 : IComparable<T2>
            public ref class BlackWhite_Compositor : Compositor
            {
             // ...
            };

              包裝

              你已經(jīng)看到了,在C++/CLI下面,你可以選擇CLR泛型或C++模板?,F(xiàn)在你所擁有的知識已經(jīng)可以根據(jù)特定的需求作出明智的選擇了。在兩種機(jī)制下,超越元素的存儲和檢索功能的參數(shù)化類型都包含了每種類型參數(shù)必須支持操作的假設(shè)。

               使用模板的時候,這些假設(shè)都是隱含的。這給模板的作者帶來了很大的好處,他們對于能夠?qū)崿F(xiàn)什么樣的功能有很大的自由度。但是,這對于模板的使用者是不利 的,他們經(jīng)常面對某些可能的類型參數(shù)上的沒有正式文檔記載的約束集合。違反這些約束集合就會導(dǎo)致編譯時錯誤,因此它對于運(yùn)行時的完整性不是威脅,模板類的 使用可以阻止失敗出現(xiàn)。這種機(jī)制的設(shè)計偏好傾向于實(shí)現(xiàn)者。

              使用泛型的時候,這些假設(shè)都被明顯化了,并與約束子句中列舉的基本類型 集合相關(guān)聯(lián)。這對泛型的使用者是有利的,并且保證傳遞給運(yùn)行時用于類型構(gòu)造的任何泛型都是正確的。據(jù)我看來,它在設(shè)計的自由度上有一些約束,并且使某些模 板設(shè)計習(xí)慣稍微難以受到支持。對這些形式約束的違反,無論使在定義點(diǎn)還是在用戶指定類型參數(shù)的時候,都會導(dǎo)致編譯時錯誤。這種機(jī)制的設(shè)計偏好傾向于消費(fèi) 者。

            posted on 2007-10-05 01:56 旅途 閱讀(261) 評論(0)  編輯 收藏 引用 所屬分類: C/C++

            久久精品成人免费网站| 久久婷婷国产剧情内射白浆| 无码八A片人妻少妇久久| 久久99精品国产麻豆蜜芽| 久久久精品免费国产四虎| 国产成人精品久久一区二区三区| 亚洲精品美女久久777777| 国产精品亚洲综合久久| 香蕉久久永久视频| 国内精品久久久久影院老司| 蜜桃麻豆WWW久久囤产精品| 人妻精品久久久久中文字幕 | 久久久久国产精品麻豆AR影院| 91精品国产91久久久久久蜜臀| 国产69精品久久久久99| 久久精品一区二区三区中文字幕| 久久夜色精品国产www| 亚洲精品国产自在久久| 97久久国产综合精品女不卡 | 四虎亚洲国产成人久久精品| 香蕉久久久久久狠狠色| 亚洲人成精品久久久久| 91精品国产91久久综合| 久久精品无码一区二区三区免费| 国产精品中文久久久久久久| 国产精品久久成人影院| 久久国产成人| 国内精品伊人久久久久av一坑| 精品无码久久久久久国产| 亚洲精品国精品久久99热一| 欧洲人妻丰满av无码久久不卡| 97久久精品人人做人人爽| 亚洲乱码日产精品a级毛片久久| 日韩人妻无码精品久久免费一| 精品免费久久久久国产一区 | 97久久超碰国产精品2021| 久久男人中文字幕资源站| 99久久99久久精品免费看蜜桃 | 久久精品国产亚洲一区二区三区| 一本色道久久HEZYO无码| 久久精品无码专区免费|