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

ivy-jie

progress ...

C++博客 首頁 新隨筆 聯系 聚合 管理
  9 Posts :: 41 Stories :: 6 Comments :: 0 Trackbacks
虛繼承與虛基類的本質
    虛繼承和虛基類的定義是非常的簡單的,同時也是非常容易判斷一個繼承是否是虛繼承
的,雖然這兩個概念的定義是非常的簡單明確的,但是在C++語言中虛繼承作為一個比較生
僻的但是又是絕對必要的組成部份而存在著,并且其行為和模型均表現出和一般的繼承體系
之間的巨大的差異(包括訪問性能上的差異),現在我們就來徹底的從語言、模型、性能和
應用等多個方面對虛繼承和虛基類進行研究。
    首先還是先給出虛繼承和虛基類的定義。
    虛繼承:在繼承定義中包含了virtual關鍵字的繼承關系;
    虛基類:在虛繼承體系中的通過virtual繼承而來的基類,需要注意的是:
            struct CSubClass : public virtual CBase {}; 其中CBase稱之為CSubClass
            的虛基類,而不是說CBase就是個虛基類,因為CBase還可以不不是虛繼承體系
            中的基類。
    有了上面的定義后,就可以開始虛繼承和虛基類的本質研究了,下面按照語法、語義、
模型、性能和應用五個方面進行全面的描述。

    1. 語法
       語法有語言的本身的定義所決定,總體上來說非常的簡單,如下:
           struct CSubClass : public virtual CBaseClass {};
       其中可以采用public、protected、private三種不同的繼承關鍵字進行修飾,只要
       確保包含virtual就可以了,這樣一來就形成了虛繼承體系,同時CBaseClass就成為
       了CSubClass的虛基類了。
       其實并沒有那么的簡單,如果出現虛繼承體系的進一步繼承會出現什么樣的狀況呢?
       如下所示:
            /*
             * 帶有數據成員的基類
             */
            struct CBaseClass1
            {
                CBaseClass1( size_t i ) : m_val( i ) {}
            
                size_t m_val;
            };
            /*
             * 虛擬繼承體系
             */
            struct CSubClassV1 : public virtual CBaseClass1
            {
                CSubClassV1( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CSubClassV2 : public virtual CBaseClass1
            {
                CSubClassV2( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
            {
                CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
            };           
            struct CDiamondSubClass1 : public CDiamondClass1
            {
                CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
            };
       注意上面代碼中的CDiamondClass1和CDiamondSubClass1兩個類的構造函數初始化列
       表中的內容。可以發現其中均包含了虛基類CBaseClass1的初始化工作,如果沒有這
       個初始化語句就會導致編譯時錯誤,為什么會這樣呢?一般情況下不是只要在
       CSubClassV1和CSubClassV2中包含初始化就可以了么?要解釋該問題必須要明白虛
       繼承的語義特征,所以參看下面語義部分的解釋。
      
    2. 語義
          在C++中虛函數必須要通過一種間接的運行時(而不是編譯時)機制才能夠激活(
      調用)的函數,而虛繼承也是必須在運行時才能夠進行定位訪問的一種體制。存在,
      但間接。其中關鍵就在于存在、間接和共享這三種特征。
       對于虛函數而言,這三個特征是很好理解的,間接性表明了他必須在運行時根據實際
       的對象來完成函數尋址,共享性表象在基類會共享被子類重載后的虛函數,其實指向
       相同的函數入口。
       對于虛繼承而言,這三個特征如何理解呢?存在即表示虛繼承體系和虛基類確實存在,
       間接性表明了在訪問虛基類的成員時同樣也必須通過某種間接機制來完成(下面模型
       中會講到),共享性表象在虛基類會在虛繼承體系中被共享,而不會出現多份拷貝。
       那現在可以解釋語法小節中留下來的那個問題了,“為什么一旦出現了虛基類,就必
       須在每一個繼承類中都必須包含虛基類的初始化語句”。由上面的分析可以知道,
       虛基類是被共享的,也就是在繼承體系中無論被繼承多少次,對象內存模型中均只會
       出現一個虛基類的子對象(這和多繼承是完全不同的),這樣一來既然是共享的那么
       每一個子類都不會獨占,但是總還是必須要有一個類來完成基類的初始化過程(因為
       所有的對象都必須被初始化,哪怕是默認的),同時還不能夠重復進行初始化,那到
       底誰應該負責完成初始化呢?C++標準中(也是很自然的)選擇在每一次繼承子類中
       都必須書寫初始化語句(因為每一次繼承子類可能都會用來定義對象),而在最下層
       繼承子類中實際執行初始化過程。所以上面在每一個繼承類中都要書寫初始化語句,
       但是在創建對象時,而僅僅會在創建對象用的類構造函數中實際的執行初始化語句,
       其他的初始化語句都會被壓制不調用。
      
    3. 模型
       為了實現上面所說的三種語義含義,在考慮對象的實現模型(也就是內存模型)時就
       很自然了。在C++中對象實際上就是一個連續的地址空間的語義代表,我們來分析虛
       繼承下的內存模型。
       3.1. 存在
           也就是說在對象內存中必須要包含虛基類的完整子對象,以便能夠完成通過地址
           完成對象的標識。那么至于虛基類的子對象會存放在對象的那個位置(頭、中間、
           尾部)則由各個編譯器選擇,沒有差別。(在VC8中無論虛基類被聲明在什么位置,
           虛基類的子對象都會被放置在對象內存的尾部)
       3.2. 間接
           間接性表明了在直接虛基承子類中一定包含了某種指針(偏移或表格)來完成通
           過子類訪問虛基類子對象(或成員)的間接手段(因為虛基類子對象是共享的,
           沒有確定關系),至于采用何種手段由編譯器選擇。(在VC8中在子類中放置了
           一個虛基類指針vbc,該指針指向虛函數表中的一個slot,該slot中存放則虛基
           類子對象的偏移量的負值,實際上就是個以補碼表示的int類型的值,在計算虛
           基類子對象首地址時,需要將該偏移量取絕對值相加,這個主要是為了和虛表
           中只能存放虛函數地址這一要求相區別,因為地址是原碼表示的無符號int類型
           的值)
       3.3. 共享
           共享表明了在對象的內存空間中僅僅能夠包含一份虛基類的子對象,并且通過
           某種間接的機制來完成共享的引用關系。在介紹完整個內容后會附上測試代碼,
           體現這些內容。
    4. 性能
       由于有了間接性和共享性兩個特征,所以決定了虛繼承體系下的對象在訪問時必然
       會在時間和空間上與一般情況有較大不同。
       4.1. 時間
           在通過繼承類對象訪問虛基類對象中的成員(包括數據成員和函數成員)時,都
           必須通過某種間接引用來完成,這樣會增加引用尋址時間(就和虛函數一樣),
           其實就是調整this指針以指向虛基類對象,只不過這個調整是運行時間接完成的。
           (在VC8中通過打開匯編輸出,可以查看*.cod文件中的內容,在訪問虛基類對象
           成員時會形成三條mov間接尋址語句,而在訪問一般繼承類對象時僅僅只有一條mov
           常量直接尋址語句)
       4.2. 空間
           由于共享所以不同在對象內存中保存多份虛基類子對象的拷貝,這樣較之多繼承
           節省空間。
    5. 應用
       談了那么多語言特性和內容,那么在什么情況下需要使用虛繼承,而一般應該如何使
       用呢?
       這個問題其實很難有答案,一般情況下如果你確性出現多繼承沒有必要,必須要共享
       基類子對象的時候可以考慮采用虛繼承關系(C++標準ios體系就是這樣的)。由于每
       一個繼承類都必須包含初始化語句而又僅僅只在最底層子類中調用,這樣可能就會使
       得某些上層子類得到的虛基類子對象的狀態不是自己所期望的(因為自己的初始化語
       句被壓制了),所以一般建議不要在虛基類中包含任何數據成員(不要有狀態),只
       可以作為接口類來提供。
               假定通過多個派生路徑繼承名為X的成員,有下面三種可能性:

            1)如果在每個路徑中X表示同一虛基類成員,則沒有二義性,因為共享該成員的單個實例;
            2)如果在某個路徑中X是虛基類的成員,而在另一路徑中X是后代派生類的成員,也沒有二義性——特定派生類實例的優先級高于共享虛基類實例。 
            3)如果沿每個繼承路徑X表示后代派生類的不同成員,則該成員的直接訪問是二義性的。




 
 


附錄:測試代碼
#include <ctime>
#include <iostream>

/*
 * 帶有數據成員的基類
 */
struct CBaseClass1
{
    CBaseClass1( size_t i ) : m_val( i ) {}

    size_t m_val;
};
/*
 * 虛擬繼承體系
 */
struct CSubClassV1 : public virtual CBaseClass1
{
    CSubClassV1( size_t i ) : CBaseClass1( i ) {}
};

struct CSubClassV2 : public virtual CBaseClass1
{
    CSubClassV2( size_t i ) : CBaseClass1( i ) {}
};

struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
{
    CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
};

struct CDiamondSubClass1 : public CDiamondClass1
{
    CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
};
/*
 * 正常繼承體系
 */
struct CSubClassN1 : public CBaseClass1
{
    CSubClassN1( size_t i ) : CBaseClass1( i ) {}
};
struct CSubClassN2 : public CBaseClass1
{
    CSubClassN2( size_t i ) : CBaseClass1( i ) {}
};
struct CMultiClass1 : public CSubClassN1, public CSubClassN2
{
    CMultiClass1( size_t i ) : CSubClassN1( i ), CSubClassN2( i ) {}
};
struct CMultiSubClass1 : public CMultiClass1
{
    CMultiSubClass1( size_t i ) : CMultiClass1( i ) {}
};
/*
 * 不帶有數據成員的接口基類
 */
struct CBaseClass2
{
    virtual void func() {};
    virtual ~CBaseClass2() {}
};
/*
 * 虛擬繼承體系
 */
// struct CBaseClassX { CBaseClassX() {i1 = i2 = 0xFFFFFFFF;} size_t i1, i2;};
struct CSubClassV3 : public virtual CBaseClass2
{
};
struct CSubClassV4 : public virtual CBaseClass2
{
};
struct CDiamondClass2 : public CSubClassV3, public CSubClassV4
{
};
struct CDiamondSubClass2 : public CDiamondClass2
{
};

/*
 * 正常繼承體系
 */
struct CSubClassN3 : public CBaseClass2
{
};
struct CSubClassN4 : public CBaseClass2
{
};
struct CMultiClass2 : public CSubClassN3, public CSubClassN4
{
};
struct CMultiSubClass2 : public CMultiClass2
{
};

/*
 * 內存布局用類聲明.
 */
struct CLayoutBase1
{
    CLayoutBase1() : m_val1( 0 ), m_val2( 1 ) {}

    size_t m_val1, m_val2;
};
struct CLayoutBase2
{
    CLayoutBase2() : m_val1( 3 ) {}

    size_t m_val1;
};
struct CLayoutSubClass1 : public virtual CBaseClass1, public CLayoutBase1, public CLayoutBase2
{
    CLayoutSubClass1() : CBaseClass1( 2 ) {}
};


#define MAX_TEST_COUNT 1000 * 1000 * 16
#define TIME_ELAPSE() ( std::clock() - start * 1.0 ) / CLOCKS_PER_SEC

int main( int argc, char *argv[] )
{
    /*
     * 類體系中的尺寸.
     */
    std::cout << "================================ sizeof ================================" << std::endl;
    std::cout << "    ----------------------------------------------------------------" << std::endl;
    std::cout << "sizeof( CBaseClass1 )       = " << sizeof( CBaseClass1 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassV1 )       = " << sizeof( CSubClassV1 ) << std::endl;
    std::cout << "sizeof( CSubClassV2 )       = " << sizeof( CSubClassV2 ) << std::endl;
    std::cout << "sizeof( CDiamondClass1 )    = " << sizeof( CDiamondClass1 ) << std::endl;
    std::cout << "sizeof( CDiamondSubClass1 ) = " << sizeof( CDiamondSubClass1 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassN1 )       = " << sizeof( CSubClassN1 ) << std::endl;
    std::cout << "sizeof( CSubClassN2 )       = " << sizeof( CSubClassN2 ) << std::endl;
    std::cout << "sizeof( CMultiClass1 )      = " << sizeof( CMultiClass1 ) << std::endl;
    std::cout << "sizeof( CMultiSubClass1 )   = " << sizeof( CMultiSubClass1 ) << std::endl;

    std::cout << "    ----------------------------------------------------------------" << std::endl;
    std::cout << "sizeof( CBaseClass2 )       = " << sizeof( CBaseClass2 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassV3 )       = " << sizeof( CSubClassV3 ) << std::endl;
    std::cout << "sizeof( CSubClassV4 )       = " << sizeof( CSubClassV4 ) << std::endl;
    std::cout << "sizeof( CDiamondClass2 )    = " << sizeof( CDiamondClass2 ) << std::endl;
    std::cout << "sizeof( CDiamondSubClass2 ) = " << sizeof( CDiamondSubClass2 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassN3 )       = " << sizeof( CSubClassN3 ) << std::endl;
    std::cout << "sizeof( CSubClassN4 )       = " << sizeof( CSubClassN4 ) << std::endl;
    std::cout << "sizeof( CMultiClass2 )      = " << sizeof( CMultiClass2 ) << std::endl;
    std::cout << "sizeof( CMultiSubClass2 )   = " << sizeof( CMultiSubClass2 ) << std::endl;
    /*
     * 對象內存布局
     */
    std::cout << "================================ layout ================================" << std::endl;
    std::cout << "    --------------------------------MI------------------------------" << std::endl;
    CLayoutSubClass1 *lsc = new CLayoutSubClass1;
    std::cout << "sizeof( CLayoutSubClass1 )   = " << sizeof( CLayoutSubClass1 ) << std::endl;
    std::cout << "CLayoutBase1 offset of CLayoutSubClass1 is " << (char*)(CLayoutBase1 *)lsc - (char*)lsc << std::endl;
    std::cout << "CBaseClass1  offset of CLayoutSubClass1 is " << (char*)(CBaseClass1  *)lsc - (char*)lsc << std::endl;
    std::cout << "CLayoutBase2 offset of CLayoutSubClass1 is " << (char*)(CLayoutBase2 *)lsc - (char*)lsc << std::endl;

    int *ptr = (int*)lsc;
    std::cout << "vbc in CLayoutSubClass1 is " << *(int*)ptr[3] << std::endl;

    delete lsc;

    std::cout << "    --------------------------------SI------------------------------" << std::endl;
    CSubClassV1 *scv1 = new CSubClassV1( 1 );
    std::cout << "sizeof( CSubClassV1 )   = " << sizeof( CSubClassV1 ) << std::endl;
    std::cout << "CBaseClass1 offset of CSubClassV1 is " << (char*)(CBaseClass1 *)scv1 - (char*)scv1 << std::endl;

    ptr = (int*)scv1;
    std::cout << "vbc in CSubClassV1 is " << *(int*)ptr[0] << std::endl;

    delete scv1;

    /*
     * 性能測試
     */
    std::cout << "================================ Performance ================================" << std::endl;
    double times[4];
    size_t idx = 0;

    CSubClassV1 *ptr1 = new CDiamondClass1( 1 );
    std::clock_t start = std::clock();
    {
        for ( size_t i = 0; i < MAX_TEST_COUNT; ++i )
            ptr1->m_val = i;
    }
    times[idx++] = TIME_ELAPSE();
    delete static_cast<CDiamondClass1*>( ptr1 );

    CSubClassN1 *ptr2 = new CMultiClass1( 0 );
    start = std::clock();
    {
        for ( size_t i = 0; i < MAX_TEST_COUNT; ++i )
            ptr2->m_val = i;
    }
    times[idx++] = TIME_ELAPSE();
    delete static_cast<CMultiClass1*>( ptr2 );

    std::cout << "CSubClassV1::ptr1->m_val " << times[0] << " s" << std::endl;
    std::cout << "CSubClassN1::ptr2->m_val " << times[1] << " s" << std::endl;

    return 0;
}

測試環境:
    軟件環境:Visual Studio2005 Pro + SP1, boost1.34.0
    硬件環境:PentiumD 3.0GHz, 4G RAM
測試數據:
================================ sizeof ================================
    ----------------------------------------------------------------
sizeof( CBaseClass1 )       = 4

sizeof( CSubClassV1 )       = 8
sizeof( CSubClassV2 )       = 8
sizeof( CDiamondClass1 )    = 12
sizeof( CDiamondSubClass1 ) = 12

sizeof( CSubClassN1 )       = 4
sizeof( CSubClassN2 )       = 4
sizeof( CMultiClass1 )      = 8
sizeof( CMultiSubClass1 )   = 8
    ----------------------------------------------------------------
sizeof( CBaseClass2 )       = 4

sizeof( CSubClassV3 )       = 8
sizeof( CSubClassV4 )       = 8
sizeof( CDiamondClass2 )    = 12
sizeof( CDiamondSubClass2 ) = 12

sizeof( CSubClassN3 )       = 4
sizeof( CSubClassN4 )       = 4
sizeof( CMultiClass2 )      = 8
sizeof( CMultiSubClass2 )   = 8
================================ layout ================================
    --------------------------------MI------------------------------
sizeof( CLayoutSubClass1 )   = 20
CLayoutBase1 offset of CLayoutSubClass1 is 0
CBaseClass1  offset of CLayoutSubClass1 is 16
CLayoutBase2 offset of CLayoutSubClass1 is 8
vbc in CLayoutSubClass1 is -12
    --------------------------------SI------------------------------
sizeof( CSubClassV1 )   = 8
CBaseClass1 offset of CSubClassV1 is 4
vbc in CSubClassV1 is 0
================================ Performance ================================
CSubClassV1::ptr1->m_val 0.062 s
CSubClassN1::ptr2->m_val 0.016 s

結果分析:
    1. 由于虛繼承引入的間接性指針所以導致了虛繼承類的尺寸會增加4個字節;
    2. 由Layout輸出可以看出,虛基類子對象被放在了對象的尾部(偏移為16),并且vbc
       指針必須緊緊的接在虛基類子對象的前面,所以vbc指針所指向的內容為“偏移 - 4”;
    3. 由于VC8將偏移放在了虛函數表中,所以為了區分函數地址和偏移,所以偏移是用補
       碼int表示的負值;
    4. 間接性可以通過性能來看出,在虛繼承體系同通過指針訪問成員時的時間一般是一般
       類訪問情況下的4倍左右,符合匯編語言輸出文件中的匯編語句的安排。
posted on 2009-06-08 22:34 ivy-jie 閱讀(190) 評論(0)  編輯 收藏 引用 所屬分類: c++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧洲99久久| 亚洲综合精品四区| 久久精品伊人| 亚洲天堂成人| 欧美国产日韩亚洲一区| 国产伦理一区| 亚洲天堂av在线免费观看| 男人天堂欧美日韩| 欧美一级片在线播放| 国产精品久久久久免费a∨大胸| 亚洲人线精品午夜| 欧美电影免费观看| 久久男女视频| 亚洲电影免费观看高清完整版在线观看| 亚洲中字在线| 亚洲一级片在线看| 国产噜噜噜噜噜久久久久久久久| 亚洲线精品一区二区三区八戒| 亚洲日本成人在线观看| 亚洲欧美制服另类日韩| 国产精品一区久久久久| 久久福利影视| 久久久午夜电影| 亚洲国产精品久久久久| 欧美激情精品久久久久久蜜臀| 美女视频网站黄色亚洲| 亚洲伦伦在线| 亚洲女同同性videoxma| 欧美日韩日日骚| 午夜久久久久| 国内自拍亚洲| 欧美91大片| 欧美激情亚洲国产| 这里只有精品视频| 亚洲午夜在线观看| 国产视频欧美| 蜜臀a∨国产成人精品| 免费久久99精品国产| 9i看片成人免费高清| 在线综合亚洲| 精品91视频| 亚洲精选久久| 韩国女主播一区二区三区| 欧美激情二区三区| 国产精品高潮在线| 欧美**人妖| 国产精品久久久久久久久借妻| 久久久精品欧美丰满| 免费观看亚洲视频大全| 亚洲免费伊人电影在线观看av| 性色av一区二区三区在线观看 | 理论片一区二区在线| 曰韩精品一区二区| 亚洲国产精品一区二区久| 午夜视黄欧洲亚洲| 亚洲国产99| 亚洲私人影院| 亚洲精品国产视频| 香蕉国产精品偷在线观看不卡| 亚洲夫妻自拍| 午夜视频在线观看一区二区三区| 亚洲精品乱码久久久久久日本蜜臀| 亚洲——在线| 一区二区三区日韩欧美精品| 久久久久久97三级| 午夜视频一区二区| 欧美日韩系列| 最新国产の精品合集bt伙计| 黑人巨大精品欧美黑白配亚洲| 一区二区日韩欧美| 日韩一级大片在线| 欧美成人激情视频免费观看| 久久精品视频免费观看| 欧美性久久久| 亚洲肉体裸体xxxx137| 影音先锋成人资源站| 亚洲淫性视频| 亚洲一区二区三区涩| 欧美精品aa| 欧美xxxx在线观看| 国产在线视频不卡二| 亚洲淫性视频| 欧美一级网站| 国产精品网红福利| 一区二区三区欧美亚洲| 99国产精品99久久久久久| 免费一级欧美在线大片| 老司机精品视频网站| 国产一区二区三区黄| 午夜精品婷婷| 久久精品国产99国产精品澳门| 国产精品免费一区豆花| 亚洲在线观看视频| 亚洲欧美日韩天堂| 国产乱码精品一区二区三区忘忧草| 一区二区三区 在线观看视| 亚洲图片自拍偷拍| 国产精品试看| 欧美综合国产| 米奇777在线欧美播放| 狠狠综合久久av一区二区小说 | 欧美精选午夜久久久乱码6080| 美女精品视频一区| 亚洲国产精品99久久久久久久久| 麻豆精品一区二区av白丝在线| 欧美激情中文字幕一区二区| 亚洲精品在线视频观看| 欧美gay视频| 亚洲免费精彩视频| 香蕉久久一区二区不卡无毒影院| 国产欧美日韩一区二区三区| 久久成人精品一区二区三区| 欧美r片在线| 一区二区三区久久网| 国产精品一区视频网站| 久久久久国产一区二区三区| 亚洲国产精品va在线看黑人动漫 | 亚洲欧洲在线看| 久久久夜夜夜| 亚洲激情校园春色| 亚洲视频一起| 国产亚洲精品资源在线26u| 久久久噜噜噜久久久| 亚洲盗摄视频| 亚洲欧美国产日韩天堂区| 狠狠色综合网| 欧美日韩在线播放一区二区| 亚洲欧美日韩一区| 亚洲丁香婷深爱综合| 亚洲专区免费| 亚洲第一在线| 国产精品日韩精品欧美精品| 久久在线视频| 亚洲欧美成人| 亚洲人成亚洲人成在线观看图片| 欧美在线一级视频| 一本久久综合亚洲鲁鲁| 狠狠色狠狠色综合日日tαg| 欧美日韩亚洲系列| 老司机午夜精品视频| 亚洲综合精品| 99ri日韩精品视频| 欧美激情中文不卡| 另类av一区二区| 久久av一区二区三区| 99综合在线| 亚洲精品免费一区二区三区| 国产一区二区三区四区| 国产精品高潮呻吟久久| 欧美激情麻豆| 久久亚洲精选| 久久国产精品99国产| 亚洲无线视频| 亚洲免费av片| 亚洲精品国产品国语在线app| 久久人人爽人人爽爽久久| 午夜欧美不卡精品aaaaa| 一本色道婷婷久久欧美| 亚洲人在线视频| 亚洲国产成人tv| 在线观看精品| 在线免费不卡视频| 黑人操亚洲美女惩罚| 国产综合自拍| 国产一区二区三区四区三区四| 国产欧美一区二区三区沐欲 | 国产日韩一区欧美| 国产精品成人播放| 欧美日韩国产123| 欧美精品亚洲二区| 欧美高清你懂得| 欧美成人一区二区三区| 可以看av的网站久久看| 久久裸体视频| 久久国产欧美日韩精品| 香蕉久久一区二区不卡无毒影院| 亚洲一区二区欧美日韩| 一区二区三区国产在线观看| 亚洲免费av电影| 亚洲视频在线看| 亚洲女同在线| 久久国产精品久久w女人spa| 久久激情五月激情| 久久九九国产精品| 久久一区国产| 欧美国产精品v| 欧美视频在线观看免费| 国产精品日韩一区二区三区| 国产日韩精品一区二区三区在线| 国产午夜精品久久久久久免费视| 国产日产亚洲精品| 国内精品久久久久国产盗摄免费观看完整版 | 欧美mv日韩mv国产网站| 免费av成人在线| 欧美国产先锋| 亚洲美女尤物影院| 亚洲免费人成在线视频观看| 欧美在线视频免费播放| 久久综合伊人77777| 欧美日韩成人一区二区三区|