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

longshanks

  C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

常用鏈接

留言簿(10)

我參與的團隊

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

C++的營養(yǎng)

莫華楓
    上一篇《C++的營養(yǎng)——RAII》中介紹了RAII,以及如何在C#中實現(xiàn)。這次介紹另一個重要的基礎(chǔ)技術(shù)——swap手法。

swap手法

    swap手法不應(yīng)當是C++獨有的技術(shù),很多語言都可以實現(xiàn),并且從中得到好處。只是C++存在的一些缺陷迫使大牛們發(fā)掘,并開始重視這種有用的手法。這 個原本被用來解決C++的資源安全和異常保證問題的技術(shù)在使用中逐步體現(xiàn)出越來越多的應(yīng)用,有助于我們編寫更加簡潔、優(yōu)雅和高效的代碼。
    接下來,我們先來和swap打個招呼。然后看看在C#里如何玩出swap。最后展示swap手法的幾種應(yīng)用,從中我們將看到它是如何的可愛。
    假設(shè),我要做一個類,實現(xiàn)統(tǒng)計并保存一個字符串中字母的出現(xiàn)次數(shù),以及總的字母和數(shù)字的個數(shù)。
        class CountStr
        
{
        
public:
            
explicit CountStr(std::string const& val)
                :m_str(val), m_nLetter(
0), m_nNumber(0{
                do_count(val);
            }

            CountStr(CountStr 
const& cs)
                :m_str(cs.m_str), m_counts(cs.m_counts)
                , m_nLetter(cs.m_nLetter), m_nNumber(cs.m_nNumber)
            
{}
               
void swap(CountStr& cs) {
                   std::swap(m_str, cs.m_str);
                   m_counts.swap(m_str);
                   std::swap(m_nLetter, cs.m_nLetter);
                   std::swap(m_nNumber, cs.m_nNumber);
               }

        
private:
            std::
string m_str;
            std::map
<charint> m_counts;
            
int m_nLetter;
            
int m_nNumber;
        }
    在類CountStr中,定義了swap成員函數(shù)。swap接受一個CountStr&類型的參數(shù)。在函數(shù)中,我們可以看到一組函數(shù)調(diào)用,每一個 對應(yīng)一個數(shù)據(jù)成員,其任務(wù)是將相對應(yīng)的數(shù)據(jù)成員的內(nèi)容相互交換。此處,我使用了兩種調(diào)用,一種是使用std::swap()標準函數(shù),另一種是通過 swap成員函數(shù)執(zhí)行這個交換。一般情況下,std::swap()通過一個臨時變量實現(xiàn)對象的內(nèi)容交換。但對于string、map等非平凡的對象,這 種交換會引發(fā)至少三次深拷貝,其復(fù)雜度將是O(3n)的,性能極差。因此,標準庫為這些類定義了swap成員函數(shù),通過成員函數(shù)可以實現(xiàn)O(1)的交換操 作。同時將std::swap()針對這些擁有swap()成員函數(shù)的標準容器特化,使其可以直接使用swap()成員函數(shù),而避免性能損失。但是,對于 那些擁有swap()成員,但沒有被特化的用戶定義,或第三方的類,則不應(yīng)使用std::swap(),而改用swap()成員函數(shù)。所以,一般情況下, 為了避免混淆,對于擁有swap()成員函數(shù)的類,調(diào)用swap(),否則調(diào)用標準std::swap()函數(shù)。
    順便提一下,在未來的C++0x中,由于引入了concept機制,可以允許一個函數(shù)模板自動識別出所有“具有swap()成員”的類型,并使用相應(yīng)的特化版本。這樣便只需使用std::swap(),而不必考慮是什么樣的類型了。
    言歸正傳。這里,swap()成員函數(shù)有兩個要求,其一是復(fù)雜度為O(1),其二是具備無拋擲的異常保證。前者對于性能而言至關(guān)重要,否則swap操作將 會由于性能問題而無法在實際項目中使用。對于后者,是確保強異常保證(commit or rollback語義)的基石。要達到這兩個要求,有幾個關(guān)鍵要點:首先,對于類型為內(nèi)置類型或小型POD(8~16字節(jié)以內(nèi))的成員數(shù)據(jù),可以直接使用 std::swap();其次,對于非平凡的類型(擁有資源引用,復(fù)制構(gòu)造和賦值操作會引發(fā)深拷貝),并且擁有符合上述要求的swap()成員函數(shù)的,直 接使用swap()成員函數(shù);最后,其余的類型,則保有其指針,或智能指針,以確保滿足上述兩個要求。
    聽上去有些復(fù)雜,但在實際開發(fā)中做到并不難。首先,盡量使用標準庫容器,因為標準庫容器都擁有滿足兩個條件的swap()成員。其次,在編寫的每一個類中 實現(xiàn)滿足兩個條件的swap()成員。最后,對于那些不具備swap()成員函數(shù)的第三方類型,則使用指針,最好是智能指針。(也就是Sutter所謂的 PImpl手法)。只要堅持這些方針,必能收到很好的效果。
    下面,就來看一下這個swap()的第一個妙用。假設(shè),這個類需要復(fù)制。通常可以通過operator=操作符,或者copy(或其他有明確的復(fù)制含義 的)成員函數(shù)實現(xiàn),這兩者實際上是等價的,只是形式不同而已。這里選擇operator=,因為它比較C++:)。
    最直白的實現(xiàn)方式是這樣:
        class CountStr
        
{
        
public:
            ...
            CountStr
& operator=(CountStr& val) {
                m_str
=val.m_str;
                m_counts
=val.m_counts;
                m_nLetter
=val.m_nLetter;
                m_nNumber
=val.m_nNumber;
            }

            ...
        }
    很簡單,但是不安全,或者說沒有滿足異常保證。
    先解釋一下異常保證。異常保證有三個級別:基本保證、強異常保證和無拋擲保證。基本保證是指異常拋出時,程序的各個部分應(yīng)當處于有效狀態(tài),不能有資源泄 漏。這個級別可以輕而易舉地利用RAII確保,這在前一篇已經(jīng)展示過了。強異常保證則更加嚴格,要求異常拋出后,程序非但要滿足基本保證,其各個部分的數(shù) 據(jù)應(yīng)保持原狀。也就是要滿足“Commit or Rollback”語義,熟悉數(shù)據(jù)庫的人,可以聯(lián)想一下Transaction的行為。而無拋擲保證要求函數(shù)在任何情況下都不會拋出異常。無拋擲保證不是 說用一個catch(...)或throw()把異常統(tǒng)統(tǒng)吞掉。而是說在無拋擲保證的函數(shù)中的任何操作,都不會拋出異常。能滿足無拋擲保證的操作還是很多 的,比如內(nèi)置POD類型(int、指針等等)的復(fù)制,swap手法便以此為基礎(chǔ)。(多說一句,用catch(...)吞掉異常來確保無拋擲并非絕對不行, 在特定情況下,還是可以偶爾一用。不過這等爛事也只能在西構(gòu)函數(shù)中進行,而且也只有在迫不得已的情況下用那么一下)。
    如果這四個賦值操作 中,任意一個拋出異常,便會退出這個函數(shù)(操作符)。此時,至少有一個成員數(shù)據(jù)沒有正確修改,而其他的則全部或部分地發(fā)生改變。于是,一部分成員數(shù)據(jù)是新 的,另一部分是舊的,甚至還有一些是不完全的。這在軟件中往往會引發(fā)很多令人苦惱的bug。無論如何,此時應(yīng)當運用強異常保證,使得數(shù)據(jù)要么是新的值,要 么沒有改變。那么如何獲得強異常保證?在swap()的幫助下,驚人的簡單:
        class CountStr
        
{
        
public:
            ...
            CountStr
& operator=(CountStr& val) {
                swap(CountStr(val)); 
// 或者CountStr(val).swap(*this);
                raturn *this;                    
            }

            ...
        }
    我想世上沒有比這等代碼更加漂亮的了吧!不僅僅具有簡潔動人的外表,而且充滿了豐富的內(nèi)涵。這就叫優(yōu)雅。不過,優(yōu)雅之下還需要一些解釋。在這兩個版本中, 都是先用復(fù)制構(gòu)造創(chuàng)建一個臨時對象,這個臨時對象同傳入的參數(shù)對象擁有相同的值。然后用swap()成員函數(shù)將this對象的內(nèi)容與臨時對象交換。于是, this對象擁有了臨時對象的值,也就是與傳入的實參對象具有相同的值(復(fù)制)。當退出函數(shù)的時候,臨時對象銷毀,自然而然地釋放了this對象原先的資 源(前提是CountStr類實現(xiàn)了RAII)。
    那么拋出異常的情況又是怎樣的呢?
    先來看看operator=里執(zhí)行了哪些步驟,并考察這些步驟的異常拋擲的情況。如果將代碼改寫成另一個等價的形式,就很容易理解了:
            CountStr& operator=(CountStr& val) {
                CountStr t_(val);    
//此處可能拋出異常,但只有t_的值發(fā)生變化
                t_.swap(*this);       //由于swap擁有無拋擲保證,所以不會拋出異常
                return *this
            }
    在構(gòu)造臨時對象的時候,可能會拋出異常,因為此時執(zhí)行了數(shù)據(jù)的復(fù)制和構(gòu)造。請注意,這時候this對象的內(nèi)容沒有改變。如果此時拋出異常,數(shù)據(jù)發(fā)生改變的 只有t_,this對象并未受到影響。而隨著棧清理,t_也將被析構(gòu),在RAII的作用下,t_所占用的資源也會依次釋放。而下一步,swap()成員的 調(diào)用,則是無拋擲保證的,不會拋出異常,this的內(nèi)容可以得到充分地、原子地交換,不會發(fā)生數(shù)據(jù)值修改一半的情況。
    在C#中,實現(xiàn)swap非常容易,甚至比C++更容易。因為在C#中,大部分對象都在堆上,代碼中定義的所謂對象實際上是引用。對于引用的賦值操作是無拋擲的,因此在C#中可以采用同C++幾乎一樣的代碼實現(xiàn)swap:
        class CountStr
        
{
            
public CountStr(string val) {
                m_str
=val;
                m_nLetter
=0;
                m_nNumber
=0;
                do_count(val);
            }

            
public CountStr(CountStr cs) {
                m_str
=new string(cs.m_str);
                m_counts
=new Dictionary<charint>(cs.m_counts);
                m_nLetter
=cs.m_nLetter;
                m_nNumber
=cs.m_nNumber
            }


              
public void swap(CountStr& cs) {
                   utility.swap(
ref m_str, ref cs.m_str);
                   utility.swap(
ref m_counts, ref cs.m_counts);
                   utility.swap(
ref m_nLetter, ref cs.m_nLetter);
                   utility.swap(
ref m_nNumber, ref cs.m_nNumber);
              }

            
public void copy(CountStr& cs) {
                
this.swap(new CountStr(cs));
            }


            
private string m_str;
            
private Dictionary<charint> m_counts;
            
private int m_nLetter;
            
private int m_nNumber;
        }
    這里utility.swap()是一個泛型函數(shù),作用是交換兩個參數(shù):
        class utility
        
{
            
public static void swap<T>(ref T lhs, ref T rhs) {
                T t_
=lhs;
                lhs
=rhs;
                rhs
=t_;
            }

        }
    如果類有關(guān)鍵性的資源需要釋放,那么可以實現(xiàn)IDisposable接口,然后在copy()中使用using:
            public void copy(CountStr& cs) {
                
using(CountStr t_=new CountStr(cs))
                    
{
                        t_.swap(
this);
                    }

            }
    如此,對象原有的數(shù)據(jù)和資源被交換到臨時對象t_中之后,在退出using作用域的時候,會立即得到釋放。這是RAII的一個應(yīng)用,詳細內(nèi)容參見本系列的前一篇《C++的營養(yǎng)——RAII》。
    swap的基本作用是維持強異常保證語義。但是,作為一種基礎(chǔ)性的技術(shù),它還可以擁有更多的用途。下面介紹幾種主要的應(yīng)用,為了節(jié)省篇幅,案例直接使用C#,不再給出C++的代碼。
    在我們的開發(fā)過程中,有時需要是一些對象復(fù)位,即回復(fù)對象的初始狀態(tài)。一般情況下,我們會在類中增加一個reset()之類的成員,在這個函數(shù)中釋放資源,恢復(fù)各成員數(shù)據(jù)的初值。但是,在擁有swap的情況下,這種操作變得非常容易:
        class X
        
{
            
public X() {
                ... 
//初始化對象
            }

            
public X(int v) {
                ... 
//以v初始化對象
            }

            
public void swap(X val) {...}
            
public void reset() {
                
this.swap(new X());
            }

            ...
        }
    reset()用X的默認構(gòu)造函數(shù)創(chuàng)建了一個臨時對象,將其內(nèi)容與this交換,this的內(nèi)容便成為了初始值。重要的是,這個成員函數(shù)也是強異常保證的。如果需要通過一些參數(shù)復(fù)位,那么同樣可以做到:
        class X
        
{
            ...
            
public void reset(int v) {
                
this.swap(new X(v));
            }

            ...
        }
    有時甚至可以不需要reset這個成員,而直接在代碼中使用swap復(fù)位一個對象:
        X x=new X();
        ... 
//對x的操作,改變了內(nèi)容
        x.swap(new X()); //復(fù)位了
    如果X有資源需要釋放,那么只需實現(xiàn)IDispose,然后使用using:
        class X : IDisposable
        
{
            ...
            
public void reset() {
                
using(X t=new X())
                
{
                    
this.swap(t);
                }

            }

            
public void Dispose() {...}
            ...
        }
    上面這些應(yīng)用都有一個共同點,即重新初始化一個對象,使其恢復(fù)到一個初始狀態(tài)。下面的應(yīng)用,則反其道而行之,將一個對象切換到另一個狀態(tài)。
    有時,我們會做一些類,在構(gòu)造函數(shù)中執(zhí)行一些復(fù)雜的操作,比如解析一個文本文件,然后向外公布解析后的結(jié)果。之后,我們需要在這個對象上load另一個文 件,那么通常都寫一個load成員函數(shù),先釋放掉原先占用的資源,然后再加載新的文件。如果有了swap,那么這個load函數(shù)同樣極其簡單:
        class Y : IDisposable
        
{
            
public Y(string filename) {
                ... 
//打開文件,執(zhí)行解析
            }

            
public void swap(Y val) {...}
            
public load(string filename) {
                
using(Y t=new Y(filename))
                
{
                    
this.swap(t);
                }

            }

            
public void Dispose() {
                ... 
//關(guān)閉文件,釋放資源
            }

        }
    還有一種情況,有一些類,通過一些數(shù)據(jù)創(chuàng)建,創(chuàng)建之后在絕大多數(shù)的情況下都是只讀的,但偶爾會需要改變其內(nèi)部數(shù)據(jù)。為了代碼的可靠性,我們可以把類寫成只讀的。但是如何修改其內(nèi)部的數(shù)據(jù)呢?也可以通過swap:
        class Z
        
{
            
public Z(int a, float b) {
                m_a
=a;
                m_b
=b;
            }

            
public void swap(Z val) {...}
            
public int a get{return m_a;}}
            
public float b get{return m_b;}}
            
private int m_a;
            
private float m_b;
        }


        Z z
=new Z(34.5);
        z.swap(
new Z(5, 5.4)); //z的值已修改
    這樣便可避免對Z的實例的隨意修改。但是,這種修改方式會造成性能損失,特別是數(shù)據(jù)成員存在非O(1)復(fù)制的情況下(如有字符串、數(shù)組等),只有在修改偶爾發(fā)生的情況下才能使用。
    有些類,構(gòu)造函數(shù)需要一些數(shù)據(jù)初始化對象,并且會創(chuàng)建的過程中會驗證其有效性,和執(zhí)行一些計算。也就是構(gòu)造函數(shù)存在一定的數(shù)據(jù)邏輯。如果需要修改對象的某 些值,會牽涉到相應(yīng)的復(fù)雜數(shù)據(jù)邏輯。通常都是把這些邏輯獨立在private成員函數(shù)中,由構(gòu)造函數(shù)和數(shù)據(jù)修改操作共享。這樣的做法往往不能帶來強異常保 證,在構(gòu)造函數(shù)里的數(shù)據(jù)驗證往往會拋出異常。因此,如果使用swap,便可以消除這類問題,并且使代碼簡化:
        class A
        
{
            
public A(int a, string b, Rectangle c) {
                ... 
//數(shù)據(jù)邏輯、計算等
            }

            
public int a {
                
setthis.swap(new A(value, m_b, m_c));}
            }

            
public string b {
                
setthis.swap(new A(m_a, value, m_c));}
            }

            
public Rectangle c {
                
setthis.swap(new A(m_a, m_b, value));}
            }

            ...
       }
    當然,也可以在類外直接進行這樣的數(shù)據(jù)設(shè)置:
        A a=new A(2"zzz", Rectangle(1,110,10));
        a.swap(
new A(3, "zzz", Rectangle(1,1, 10,10)));
    這種用法可以用于某些只保存對構(gòu)造函數(shù)參數(shù)的計算結(jié)果,而不需要保存這些參數(shù)的類(m_a,m_b,m_c都不需要了),只是使用上過于瑣碎。
    所有這些與對象狀態(tài)設(shè)置有關(guān)的swap用法,都集中表現(xiàn)了一個特性,即使得我們可以將對象的初始化代碼集中在構(gòu)造函數(shù)中,數(shù)據(jù)和資源清理的代碼集中在 Dispose()中。這種做法可以大大提高代碼的可維護性。如果一個軟件項目中,每個類都實現(xiàn)swap和復(fù)制構(gòu)造函數(shù)(除非該類不允許復(fù)制),并盡可能 集中數(shù)據(jù)邏輯代碼,那么會使得代碼質(zhì)量有答復(fù)的提高。
    在上一篇《C++的營養(yǎng)——RAII》中,我提到守衛(wèi)一個結(jié)構(gòu)復(fù)雜的類:在代碼中修改一個對象,然后再回復(fù)原來的狀態(tài)。如果單純手工地保存對象數(shù)據(jù),通常 很困難(有時甚至是不可能的),而且也難以維持異常安全性(強異常保證)。但是如果使用了swap,那么將會易如反掌:
        void ScopeObject(MyCls obj) {
            
using(MyCls t_=new MyCls(obj))
            
{
                ... 
//操作obj,改變其狀態(tài)或數(shù)據(jù)
                obj.swap(t_); //恢復(fù)原來的狀態(tài)
            }

        }
    當然,也可以直接使用t_執(zhí)行操作,這就不需要執(zhí)行swap。在一般情況下兩者是等價的。但是,在某些特殊情況下,比如類持有特殊資源,或者obj是并發(fā) 中的共享對象的時候,兩種方法有可能不等價。swap方案使用上更全面些。總的來說相差不多,放在這里僅供參考。
    作為更進一步的發(fā)展,可以構(gòu)造一個ISwapable泛型接口:
        interface ISwapable<T>
        
{
            
void swap(T v);
        }
    對于需要實現(xiàn)swap手法的類,實現(xiàn)這個接口:
        class B : ISwapable<B>
        
{
            
public B() {...}
            
public void swap(B v) {...}
            ...
        }
    這將會帶來一個好處,通過泛型算法實現(xiàn)某些特定的操作:
        class utility
        
{
            
public static void reset<T>(T obj)
                
where T : ISwapable
                
where T : new()
            
{
                obj.swap(
new T());
            }

        }
    這樣便無須為每一個類編寫reset成員函數(shù),只需這一個泛型算法即可:
        X x;
        Y y;
        utility.reset(x);
        utility.reset(y);
        ...
    swap手法可能在存在其他諸多應(yīng)用,在編碼的時候可以不斷地發(fā)掘。只需要抓住一個原則:swap可以無拋擲,簡潔地修改一個對象的值。swap所帶來的 一個問題主要是性能方面。swap通常伴隨著臨時對象的構(gòu)造,多數(shù)情況下,這種構(gòu)造不會引發(fā)更多的性能損失,但在某些數(shù)據(jù)修改的應(yīng)用中,會比直接的數(shù)據(jù)修 改損失更多的性能。如何取舍,需要根據(jù)具體情況分析和權(quán)衡。總的來說,swap手法所帶來的好處是顯而易見的,特別是強異常保證,往往是至關(guān)重要的。而諸 如簡化代碼等的作用,則無需多言,一用便知。
    或許swap手法非常基礎(chǔ),非常細小,而且很多人不用swap也過來了。但是,聚沙成塔,每一處細小的優(yōu)化,積累起來則是巨大的進步。還是劉皇叔說得好:“勿以善小而不為,勿以惡小而為之”。
posted on 2008-02-26 15:16 longshanks 閱讀(3909) 評論(3)  編輯 收藏 引用

Feedback

# re: C++的營養(yǎng)——swap手法[未登錄] 2008-02-26 16:41 heroboy
map重載了std::swap,所以一般情況下都可以用std::swap,沒有必要用成員函數(shù)。  回復(fù)  更多評論
  

# re: C++的營養(yǎng)——swap手法[未登錄] 2008-02-26 16:56 heroboy
swap就做swap的功能吧,擴展太多功能的話,代碼不容易讀懂。
c#其實沒必要用ISwapable接口,用反射的話每個對象都可以swap。  回復(fù)  更多評論
  

# re: C++的營養(yǎng)——swap手法 2008-03-09 16:47 DK_jims
C# 在中間語言上,每個類都有個tostirng的~ 還要RTTI?  回復(fù)  更多評論
  


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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网站| 国产欧美日韩精品a在线观看| 久久综合激情| 欧美久久久久久久久久| 久久精品久久综合| 欧美黄色日本| 欧美中文在线观看国产| 久久人人爽爽爽人久久久| 99视频+国产日韩欧美| 亚洲在线观看免费视频| 亚洲激情av| 亚洲欧美激情精品一区二区| 亚洲区一区二区三区| 亚洲伊人第一页| 亚洲美女精品成人在线视频| 午夜精品久久久久久久久久久久| 亚洲精品国产精品国自产观看| 中文精品一区二区三区| 亚洲精品1234| 亚洲自拍16p| 日韩一级在线观看| 久久久亚洲国产天美传媒修理工| 亚洲欧美日本视频在线观看| 男人插女人欧美| 久久一区二区三区国产精品| 欧美日韩精品免费在线观看视频| 国产乱码精品一区二区三区av| 欧美专区福利在线| 一二三区精品福利视频| 欧美伊人精品成人久久综合97| 一区二区三区黄色| 久热成人在线视频| 久久久久网站| 国产农村妇女毛片精品久久莱园子| 亚洲国产美女久久久久| 韩国女主播一区二区三区| 亚洲一级黄色片| 亚洲一区二区高清| 欧美另类在线播放| 亚洲国产黄色| 91久久精品日日躁夜夜躁欧美| 久久精品免费电影| 久久精品国产99精品国产亚洲性色 | 最新亚洲电影| 久久国产一区二区| 久久精品国产99国产精品澳门| 国产精品色午夜在线观看| 99精品欧美一区二区三区综合在线| 亚洲老板91色精品久久| 欧美大胆a视频| 亚洲国产精品高清久久久| 亚洲精品日本| 亚洲激情六月丁香| 亚洲精品极品| 欧美日韩精品免费观看视一区二区 | 一区二区在线免费观看| 久久久精品一区| 欧美电影在线观看完整版| 亚洲第一区中文99精品| 欧美激情精品久久久久久黑人| 亚洲黄色一区| 亚洲在线一区二区三区| 国产精品一区二区三区四区 | 亚洲欧美视频一区二区三区| 欧美一区二区三区在线观看| 国产亚洲女人久久久久毛片| 久久裸体艺术| 亚洲国产精品久久久久| 宅男精品视频| 国产女主播一区| 久久在线视频在线| 亚洲人成小说网站色在线| 亚洲私人影院在线观看| 国产精品一二三视频| 久久精品人人| 亚洲精品一区二区三区不| 午夜精品免费在线| 在线不卡欧美| 欧美婷婷久久| 久久精品国产亚洲一区二区| 亚洲欧洲精品一区二区| 欧美在线地址| 亚洲日本乱码在线观看| 国产精品永久免费| 嫩草伊人久久精品少妇av杨幂| 一区二区黄色| 欧美aaa级| 性欧美激情精品| 亚洲肉体裸体xxxx137| 国产精品欧美日韩久久| 美女主播一区| 亚洲欧美在线免费观看| 亚洲精品日韩在线| 久久深夜福利免费观看| 亚洲一区在线直播| 亚洲欧洲在线播放| 国产在线拍揄自揄视频不卡99| 欧美理论电影网| 久久伊人一区二区| 香蕉久久国产| 亚洲视频精品在线| 亚洲国产专区校园欧美| 久久免费午夜影院| 性高湖久久久久久久久| 亚洲精品一区二区三区四区高清 | 欧美日本韩国一区| 美国十次成人| 久久久久国产精品一区二区| 亚洲在线视频观看| 一区二区免费在线观看| 亚洲国产导航| 欧美国产综合| 欧美国产一区在线| 欧美高清在线播放| 免费日韩av| 免费不卡在线视频| 久久人人看视频| 久久久精品国产免大香伊 | 一本色道久久综合精品竹菊 | 国产日产欧美精品| 精品va天堂亚洲国产| 亚洲一区二区三| 亚洲国产成人精品久久久国产成人一区| 国产精品男女猛烈高潮激情| 欧美日韩三级视频| 欧美日韩在线电影| 欧美视频在线观看一区| 欧美视频在线免费看| 欧美三级日韩三级国产三级| 欧美色欧美亚洲高清在线视频| 欧美日韩精品国产| 欧美视频在线观看| 国产精品腿扒开做爽爽爽挤奶网站| 国产精品乱码妇女bbbb| 国产精品私人影院| 国产一区二区剧情av在线| 国产一区在线免费观看| 激情欧美一区二区| 亚洲日本国产| 亚洲天堂网在线观看| 午夜精品久久| 久久久亚洲国产天美传媒修理工 | 亚洲精品久久久久久一区二区| 亚洲精品网站在线播放gif| 一本色道久久加勒比精品| 亚洲一区二区三区四区中文 | 欧美中文字幕在线视频| 久久天堂国产精品| 91久久精品日日躁夜夜躁国产| 亚洲欧洲日产国码二区| 亚洲一区二区三区精品视频| 香蕉尹人综合在线观看| 另类尿喷潮videofree| 欧美日本一区二区高清播放视频| 国产精品美女午夜av| 国产综合网站| 亚洲最快最全在线视频| 欧美一级视频免费在线观看| 免费av成人在线| 日韩视频一区二区三区在线播放免费观看 | 亚洲欧美韩国| 久热国产精品| 亚洲精品视频啊美女在线直播| 亚洲欧美激情精品一区二区| 久久久久网址| 国产精品久久久久99| 一区二区三区在线观看欧美| 9久re热视频在线精品| 久久激情五月丁香伊人| 亚洲激情综合| 久久激情一区| 国产精品成人一区| 亚洲第一色在线| 欧美一区1区三区3区公司| 欧美国产综合一区二区| 香蕉国产精品偷在线观看不卡 | 亚洲欧美制服另类日韩| 欧美激情综合五月色丁香| 国产午夜精品美女毛片视频| 一二三区精品| 亚洲国产另类久久精品| 久久精品成人一区二区三区| 欧美色偷偷大香| 尤物yw午夜国产精品视频| 亚洲欧美日韩在线观看a三区|