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

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            仿函數(shù)、綁定、橋接、委托相關(guān)討論

            from:http://www.gmdev.cn/program/index.html

            仿函數(shù)、綁定、橋接、委托相關(guān)討論:

            以下隨便討論下,沒突出的中心論點,個中理論只代表我個人觀點,難免有錯:),歡迎指正。

            一。需求:

            在事件處理常常會碰到這樣的情況:

            1。接口分離。即invokers(調(diào)用者)與(receivers)接收者分離。

            2。時間分離。

            比如說:UI相關(guān)元素(按鈕、菜單等)就是一個invokers。

            receivers則是響應(yīng)命令的對象(如對話框或應(yīng)用程序本身)。

            這需要我們要先將UI相關(guān)元素的事件響應(yīng)的接收者在初始化時先保存起來。

            待后用戶按下按鈕等再觸發(fā)(即invokers通過調(diào)用對應(yīng)先前保存的receivers來執(zhí)行命令)

            嗯,在delphi、java、vcl、.net中有相關(guān)的實現(xiàn)。而vc則需要自己來弄。

            二。仿函數(shù)的實現(xiàn):

            在說仿函數(shù)前先說說我們應(yīng)該怎么保存這些操作相關(guān)函數(shù)的呢?

            // 一般的函數(shù)我們可以這么存:

            void (*fun)() = Test;

            (*fun)();

            // 而類成員函數(shù)可以這么做:

            void (CTest::*mfn)(); // 或用 typedef void (CTest::*MFN_TEST)(); MFN_TEST mfn;

            mfn = CTest::Test;

            CTest a, *p=new CTest;

            (a.*mfn)(); // 調(diào)用方法1

            (p->*mfn)(); // 調(diào)用方法2

            如上所述可見為了處理前面所述的事件響應(yīng)情況,我們通常會用回調(diào)函數(shù),

            就是把類成員函數(shù)定義為靜態(tài)函數(shù),在初始時保存函數(shù)地址(與一般函數(shù)處理類同)及對應(yīng)的對象指針,

            在事件觸發(fā)時調(diào)用對應(yīng)的靜態(tài)函數(shù),而該函數(shù)中在把指針強制轉(zhuǎn)化為對應(yīng)類型對象地址,

            得以操縱該對象的成員變量(嗯,理論上跟成員函數(shù)的實現(xiàn)差不多,成員函數(shù)會由編譯器安插一個

            this指針作為第1個參數(shù)傳給函數(shù),以便可以操作該this對象的成員)。

            回調(diào)函數(shù)應(yīng)用的具體代碼如下:

            1). 回調(diào)接口(靜態(tài)函數(shù)法):

            //======================================================

            #include "stdafx.h"

            #include

            typedef void(*KEY_RESPOND)(void* /*,param*/);

            struct CListener

            {

            void* pThis;

            KEY_RESPOND pfn;

            CListener() : pThis(0), pfn(0){}

            };

            class CInput

            {

            std::list m_listListener;

            public:

            void AddListener( CListener* pListener ){

            m_listListener.push_back( pListener );

            }

            void RemoveListerner( CListener* pListener ){

            std::list::iterator iter;

            for( iter = m_listListener.begin(); iter!= m_listListener.end();iter++ ){

            if( pListener == (*iter) ){

            m_listListener.erase( iter ); break;

            }

            }

            }

            void HitOneKey(){

            std::list::iterator iter;

            for( iter = m_listListener.begin(); iter!= m_listListener.end();iter++ ){

            if( (*iter)->pfn && (*iter)->pThis ){

            (*(*iter)->pfn)( (*iter)->pThis );

            }

            }

            }

            void clearListener(){

            m_listListener.clear();

            }

            };

            class CUI

            {

            public:

            static void InputProc(void* pThis/*,param*/){

            __asm int 3 // 下個斷點測試下(某些編譯器不能這么寫,vc可以)

            }

            };

            CUI ui;

            CInput input;

            int _tmain(int argc, _TCHAR* argv[])

            {

            // 初始:

            CListener* pListener = new CListener;

            pListener->pfn = &CUI::InputProc;

            pListener->pThis = &ui;

            // 觸發(fā):

            input.AddListener( pListener ); // input即為invokers(調(diào)用者,但叫觸發(fā)者好點)

            input.HitOneKey(); // 某處事件觸發(fā),內(nèi)部呼叫receivers(這里是原先保存的CUI對象)來真正處理該事件(InputProc(...)方法)。

            // 清除

            input.clearListener();

            if( pListener )

            {

            delete pListener;

            pListener = NULL;

            }

            return 0;

            }

            //======================================================

            // 第2種方法: 回調(diào)類(虛函數(shù)多態(tài)法):

            //======================================================

            class IWillBack

            {

            public:

            virtual void InputProc(/*參數(shù)略...*/){}

            };

            class CInput

            {

            public:

            void RegisterListener(IWillBack* pListener){

            m_pListener = pListener; // 這里用list存起來才好,這里只作測試

            }

            void OnInputEvent(){

            m_pListener->InputProc(/*參數(shù)略...*/);

            }

            private:

            IWillBack* m_pListener;

            };

            class CUI : public IWillBack

            {

            public:

            void InputProc(/*參數(shù)略...*/){ /*..實際處理代碼..*/}

            private:

            };

            int _tmain(int argc, _TCHAR* argv[])

            {

            CInput aa;

            CUI bb;

            aa.RegisterListener(&bb);

            aa.OnInputEvent();

            return 0;

            }

            //======================================================

            但是第1種靜態(tài)函數(shù)用法是不直觀的,第2種需要派生增加了之間的聯(lián)系,而為了方便我們通常會將成員函數(shù)指針轉(zhuǎn)化為函數(shù)對象來處理,即仿函數(shù)(一般是指重載了()操作符的類)來實現(xiàn)。

            類似于這樣的操作,stl提供了mem_fun、mem_fun_ref、binder1st、binder2nd簡單操作。

            但stl的方法相對比較原始而受限制,比如說std::mem_fun需要成員函數(shù)有返回值,

            std::mem_fun最多只能支持成員函數(shù)有一個參數(shù)等,

            下面來看std:mem_fun_ref不支持成員函數(shù)返回值為void的一個例子:

            //======================================================

            #include

            class CFoo

            {

            public:

            void test() // 只有將void改成別的類型才可以,如:int

            {

            return 0;

            }

            };

            void main()

            {

            CFoo t;

            std::mem_fun_ref(&CFoo::test)(t);

            }

            //======================================================

            上述代碼只有將void改成別的類型(如int)才可以,

            那么為什么不可以處理返回void的函數(shù)呢? stl的實現(xiàn)究竟是怎么樣的呢?

            嗯,stl簡單實現(xiàn)了mem_fun_ref及mem_fun,其中mem_fun_ref以引用方式處理函數(shù)所屬對象,

            而mem_fun以指針方式處理函數(shù)所屬對象。

            現(xiàn)在讓我們從vc的stl挖出部份代碼來看看,

            1.stl的實現(xiàn):

            以mem_fun_ref為例(省略某些對說明不重要的細(xì)節(jié),兩條虛線包括的代碼為stl類似源碼):

            //======================================================

            //----------------------------------------------------------

            namespace stl_test

            {

            // 主要實現(xiàn):

            template

            class mem_fun_ref_t

            {

            public:

            mem_fun_ref_t( R (T::*_Pm)() ) : _Ptr(_Pm) {} // 構(gòu)造: 保存成員函數(shù)地址

            R operator()(T& _X) const // 調(diào)用: 這里可看出mem_fun_ref以引用方式處理

            {

            return ((_X.*_Ptr)()); // 這里執(zhí)行調(diào)用函數(shù),并返回該函數(shù)所返回值

            }

            private:

            R (T::*_Ptr)(); // 指向成員函數(shù)地址的指針

            };

            // 這里只是利用函數(shù)的參數(shù)推導(dǎo)來自動獲取型別(方便調(diào)用)

            template inline

            mem_fun_ref_t mem_fun_ref(R (T::*_Pm)())

            {

            return (mem_fun_ref_t(_Pm));

            }

            } // end of namespace test_stl

            //----------------------------------------------------------

            class CFoo

            {

            public:

            int test1(){

            __asm int 3

            return 0;

            }

            void test2(){

            __asm int 3

            }

            };

            int APIENTRY WinMain(HINSTANCE hInstance,

            HINSTANCE hPrevInstance,

            LPSTR lpCmdLine,

            int nCmdShow)

            {

            CFoo t;

            stl_test::mem_fun_ref( &CFoo::test1 ) (t);

            return 0;

            }

            //======================================================

            /////////////////////////////////////////////////////////////////

            從源碼"return ((_X.*_Ptr)()); " 可以看到stl直接返回該函數(shù)所返回值。

            所以函數(shù)沒有返回值(即為void時)的話編譯器就會報錯。好,那么如果我們在

            這里只是直接執(zhí)行函數(shù)而不用return返回的話編譯器應(yīng)該可以通過了。

            嗯,boost中正是這么處理的。(btw.為了更為通用,boost對stl原有仿函數(shù)及綁定作了大量的改進(jìn)工作)。

            但是具體應(yīng)該怎么區(qū)分有沒有返回值呢?這個也容易,我們只需用到模板的偏特性就可

            以做到。下面就看看boost的實現(xiàn)(btw.boost有兩種版本,我用的是兼容版本,代碼難看)

            2. boost的實現(xiàn)(這里我把boost的一大堆宏(真@$@#@#難看,loki在這方面來得比較清爽)去掉了):

            /////////////////////////////////////////////////////////////////

            // notreturn.cpp : Defines the entry point for the application.

            //

            #include "stdafx.h"

            //------------------------------------

            namespace boost_test

            {

            template // 有返回值時會調(diào)用這個

            struct mf

            {

            template

            class inner_mf0

            {

            R (T::*_Ptr)();

            public:

            inner_mf0(R (T::*f)()) : _Ptr(f) {}

            R operator()(T& X) const

            {

            return ((X.*_Ptr)());

            }

            };

            };

            template<> // 沒有反回值時會調(diào)用這個

            struct mf // 偏特化

            {

            template

            class inner_mf0

            {

            R (T::*_Ptr)();

            public:

            inner_mf0(R (T::*f)()) : _Ptr(f) {}

            R operator()(T& X) const

            {

            ((X.*_Ptr)());

            }

            };

            };

            // 創(chuàng)建一派生類,派生于上述基類

            template

            struct mf0 : public mf::inner_mf0

            {

            typedef R(T::*F)();

            explicit mf0(F f) : mf::inner_mf0(f) {}

            };

            // 通過函數(shù)的參數(shù)推導(dǎo)自動獲取類型

            template

            mf0 mem_fn( R(T::*f)() )

            {

            return mf0(f);

            }

            } // namespace boost_test

            //------------------------------------

            class CFoo

            {

            public:

            int test1(){ return 0; }

            void test2(){}

            };

            int APIENTRY WinMain(HINSTANCE hInstance,

            HINSTANCE hPrevInstance,

            LPSTR lpCmdLine,

            int nCmdShow)

            {

            CFoo t;

            boost_test::mem_fn( &CFoo::test1 ) (t);

            return 0;

            }

            /////////////////////////////////////////////////////////////////

            從上述代碼可以看到偏特性幫助我們解決了返回值為void的情況。但是手寫了兩份

            基本相同的代碼。。。

            另外處理參數(shù)個數(shù)的情況也很容易,只要分別實現(xiàn)不同參數(shù)的各個模板類就可以了,

            boost最多只能支持成員函數(shù)有8個參數(shù),因為它內(nèi)部實現(xiàn)了8份這樣不同參數(shù)模板類。

            其實的處理方法都是一模一樣的,可是由于語言的限制我們還是沒有辦法不一一實現(xiàn)

            不同參數(shù)的類:。在loki中參數(shù)可以用TList實現(xiàn)任意的參數(shù),但是在實現(xiàn)還是得老

            老實實的每份手寫一份(loki實現(xiàn)了15份可以支持15個參數(shù))。

            這真讓人郁悶。。。不過沒辦法。

            說完來仿函數(shù),下面開始說說有關(guān)綁定,stl、boost、loki的綁定的意思是

            對某物實體的“綁定”,通俗來說是指對函數(shù)、構(gòu)造函數(shù)、仿函數(shù)等與其對應(yīng)的某個參數(shù)的綁定,

            以便在調(diào)用時不用再次輸入此參數(shù)(因為某些時候參數(shù)是固定的,比如說綁定一個內(nèi)部存有

            成員函數(shù)地址的仿函數(shù)和它對應(yīng)的對象地址在一起)。

            以下是stl的bind用法:

            //================================

            #include "stdafx.h"

            #include // stl

            #include // boost

            struct CFoo {

            int test(int){

            return 0;

            }

            };

            void main()

            {

            boost::function1 f; // 這里用了boost

            CFoo obj;

            f = std::bind1st(std::mem_fun(&CFoo::test), &obj);

            f(5);

            }

            //================================

            loki中的BindFirst比較類似于stl的binder

            (binder1st,binder2nd),但是它是通用的,可以通過嵌套實現(xiàn)任意多個參數(shù)綁定:

            //================================

            void f()

            {

            Functor cmd1(something);

            Functor cmd2(BindFirst(cmd1, 10));

            cmd2(20);

            Functor cmd3(BindFirst(cmd2, 30));

            cmd3();

            }

            而boost中的實現(xiàn)是以占位符來表現(xiàn),具體如何實現(xiàn),下回繼續(xù)討論(嗯,

            boost代碼的宏太多了,這部份還是等有空再補全了,現(xiàn)在我們來看看如何實現(xiàn)一個委托類)

            三。委托類的實現(xiàn):

            1. 橋接模式:

            設(shè)計模式告訴我們可以使用橋接模式(Bridge Pattern)減少對象之間的 耦合度,橋接模式如下:

            Invoker <>------------------->* Interface

            ^

            |

            Receiver

            上圖的Invoker表示事件觸發(fā)者,Receiver表示事件處理者,符號類似于<c++大規(guī)模編程。。>一書所描述(注:這里符號對位可能出錯:變成左對齊了),

            其中<>------------------->表示Invoker 內(nèi)含(擁用)Interface(即Invoker 有Interface的變量或指針并負(fù)責(zé)Interface的釋放),

            而*號表示可有多個。

            ^

            | 號則表示繼承于(Receiver繼承于Interface)。

            好,我們先來分析前面在" 第2種方法: 回調(diào)類(虛函數(shù)多態(tài)法):"的實現(xiàn)思想(請回到前面看看代碼),

            它其實就是一個橋接模式,如下(括號內(nèi)對應(yīng)前面所實現(xiàn)的類):

            Invoker(CInput) <>--------------->* Interface(IWillBack)

            ^

            |

            Receiver(CUI)

            對照我們前面實現(xiàn)的代碼可以發(fā)現(xiàn)此種實現(xiàn)的橋接的缺點是:每一個想要

            注冊一方法到Invoker中以便Invoker在事件觸發(fā)時調(diào)用的類(如Receiver)都要派生自Interface。

            有沒有更好的辦法再次減少這種耦合度呢?這正是下面我們要討

            論的下一種設(shè)計模式:

            2. 委托與事件:

            委托的處理設(shè)計如下:

            Invoker <>--------------------->* Interface

            ^

            |

            Implementation -----------------> Receiver

            即在原橋接模式下再加一層間接性:Implementation 。其中

            Implementation與Receiver之間的----------------->表示Implementation引用了Receiver一些服務(wù),

            即Implementation用到了Receiver某些東西(如函數(shù)或數(shù)據(jù))。嗯,這些解釋不知是否適當(dāng),希望不會誤導(dǎo)。。。

            好,一開始可能我們會這么設(shè)計:

            //======================================================================================

            class handle {};

            template

            class Implementation : public handle

            {

            T* m_pThis;

            public:

            Implementation ( T* pThis ) : m_pThis(pThis) {}

            template

            void execute( void (T::*mfn)(T1), T1 arg ) { (m_pThis->*mfn)( arg ); }

            };

            struct Receive {

            void Proc(int) {

            __asm int 3

            }

            };

            Receive a;

            void Invoker(){

            Implementation test = Implementation (&a);

            test.execute( Receive::Proc, 10 ); // 當(dāng)事件發(fā)生時調(diào)用

            };

            int _tmain(int argc, _TCHAR* argv[])

            {

            Invoker();

            }

            //======================================================================================

            但是Invoker知道了太多Receive的信息,況且我們想讓觸發(fā)者Invoker作成一個類。

            一個改進(jìn)的版本如下:

            //-------------------------------------------------------------

            // signal slot system

            // 注: 該法我是看了"落木隨風(fēng)"的"委托、信號和消息反饋的模板實現(xiàn)技術(shù)",

            // 代碼作了部份添加。在這里非常的感謝他!

            // 他的博客:http://blogs.gcomputing.com/rocwood/archives/000154.html

            //-------------------------------------------------------------

            // Delegation.cpp : Defines the entry point for the console application.

            //

            #include

            #ifndef K_DELEGATE_H

            #define K_DELEGATE_H

            namespace kUTIL

            {

            // 1. 橋接類(純虛類):

            // 為什么叫作橋接?

            // 因為通過它的虛函數(shù)方法可以調(diào)用到對應(yīng)的正確的不同派生實例(指后面

            // 提到的委托人)所改寫的虛函數(shù)方法(這是委托人用來完成委托任務(wù)的方法)

            struct kDelegationInterface

            {

            virtual ~kDelegationInterface() {};

            virtual void Execute() = 0;

            };

            // 2. 委托類(派生于橋接類,這里我叫為”委托人“)

            // 為什么叫委托?

            // 因為調(diào)用者把“通知”的工作委托給它來負(fù)責(zé)處理。

            // 一個“委托人”保存了: a.”接收者“(對象指針m_pThis) 及 b.“要作的事”(方法指針m_mfn),

            // 以便調(diào)用者發(fā)出信號彈(后面提到,信號彈有一個作橋接用的純虛類的指針指向相應(yīng)的委托人)

            // 告知此信號對應(yīng)的委托人來完成它被委托的工作:即讓“接收者”(m_pThis)作”要作的事“(m_mfn)。

             

            template

            struct kDelegationImpl : public kDelegationInterface

            {

            typedef 
            void ( T::* MFN )();

            kDelegationImpl( T
            * pthis, MFN mfn ) : m_pThis( pthis ), m_mfn( mfn ) {

            }


            virtual void Execute() {

            if( m_pThis ) 

            ( m_pThis
            ->*m_mfn )(); 

            }


            }


            T
            * m_pThis;

            MFN m_mfn;

            }
            ;

             

            // 3. 信號彈(實現(xiàn)為仿函數(shù)來調(diào)用統(tǒng)一的虛函數(shù)接口):

            // 為什么叫信號?

            // 因為當(dāng)"信號彈"發(fā)射時(調(diào)用信號的操作符"()")

            // 它會通知所指向的"委托人"事件發(fā)生了(調(diào)用純虛類指針的m_DI->Execute()方法)。

            // 一個信號保存了一個指向?qū)?yīng)”委托人“的橋接類(純虛類)指針。

             

            struct kSignal0

            {

            kDelegationInterface
            * m_DI; // 純虛類的指針

            kSignal0() : m_DI(
            0{}

             

            // 1. 純虛類的m_DI指針可以指向不同的派生實例:

             

            template

            void ConnectSlot(T* recv, void (T::* mfn)()) {

            DisConnect();

            m_DI 
            = new kDelegationImpl( recv, mfn );

            int test = 0;

            }


            void DisConnect() {

            if( m_DI ) { delete m_DI; m_DI = NULL; }

            }

             

            // 2. 用統(tǒng)一的純虛類指針調(diào)用不同派生類改寫的虛函數(shù)

             

            void operator() () 

            if( m_DI ) {

            m_DI
            ->Execute(); 

            }


            }


            };

             

            // 下面是兩個為方便使用的函數(shù):

             

            template

            void kConnect( kSignal0& sgn, T* pObj, void(T::*fn)())

            {

            sgn.ConnectSlot( pObj, fn );

            int i = 0;

            }


            inline 
            void kDisConnect( kSignal0& sgn )

            {

            sgn.DisConnect();

            }


            // end of namespace kUTIL

            #endif //#ifndef K_DELEGATE_H

             

             

            //----------------------------------------------------------------------------

            // 一個使用實例:

            class kButton {

            public:

            kUTIL::kSignal0 sgnMouseBtnUp;

            void OnMouseButtonUp() { sgnMouseBtnUp(); }

            }
            ;

            class kDialog {

            kButton btn;

            public:

            kDialog() 
            {

            kUTIL::kConnect( btn.sgnMouseBtnUp, 
            this&kDialog::DoWork ); // vc6下這里kDialog::DoWork的前面一定要可加"&"號

            }


            void DoWork() 

            __asm 
            int 3

            }


            void TestMouseHit() { btn.OnMouseButtonUp(); }

            }
            ;

            int main(int argc, char* argv[])

            {

            kDialog dlg;

            kButton btn;

            kUTIL::kConnect( btn.sgnMouseBtnUp, 
            &dlg, kDialog::DoWork ); // vc6下這里kDialog::DoWork的前面可加/不加"&"號

            // 測試一:



            // 測試二:

            dlg.TestMouseHit();

            return 0;

            }

             

             

            // 委托實例總結(jié):

            // 下面我們來具體說明”當(dāng)某事發(fā)生時,調(diào)用者發(fā)射信號彈通知對應(yīng)的接收者作相應(yīng)處理“

            // 1. "調(diào)用者" 擁有各種信號彈。

            // 2. 初始時,我們把信號彈與對應(yīng)的委托人聯(lián)系起來,并讓委托人記錄在信號觸發(fā)時應(yīng)該通知的"接收人"和"接收人該作的事"。

            // a. 信號彈保存了橋(純虛類)指針,指針指向通過其模板函數(shù)ConnectSlot方法來找出(產(chǎn)生的)委托人(委托實例)。

            // b. 委托人(委托實例)在信號彈用ConnectSlot方法產(chǎn)生它的時候保存了函數(shù)ConnectSlot所傳入的兩個參數(shù):

            // 即"接收者指針"及"其方法指針"。

            // 3. 當(dāng)事件發(fā)生時"調(diào)用者"發(fā)射對應(yīng)信號彈后,信號彈會調(diào)用其所保存的純虛類指針的虛函數(shù)方法,

            // 于是由于虛函數(shù)特性就會調(diào)用到其所指向的委托實例(委托人)所改寫的方法。

            // 5. 委托人改寫的方法中通過其所保存的”接收者指針“及其"方法指針"來呼叫"接收者"用對應(yīng)的”方法指針“

            // 來處理事情。

            // 即如下流程:

            // "調(diào)用者"發(fā)射"信號彈" ---> "信號彈"通過"橋"找到對應(yīng)"委托人" ---> "委托人"呼叫"接收者"作"該作的事"

            //=============================================================================

            嗯,這樣到此,一個非常方便的委托類就得以實現(xiàn)了!如果你還不懂的話請仔細(xì)的琢磨,如此精華(因為簡單而強大)

            不要錯過。不過上述只是部份實現(xiàn),當(dāng)你要支持帶參數(shù)及返回值的各種情況的話,還得自己作擴充。

            返回值的處理方法可參見前面剖述boost的mem_fn的處理方法,而帶不同參數(shù)的處理則只能一一手動

            實現(xiàn),就象前面所說的那樣,這是很無奈的事情,但是目前來說沒有辦法。。。

            (待續(xù)。。。。。。,如果有時間有必要的話。。。)

            附:

            loki下載:

            http://sourceforge.net/projects/loki-lib/

            boost下載:

            http://sourceforge.net/projects/boost/

            2004.10.26更新:

            修正:

            原void connect( Signal0& sgn,T1 obj, void(T2::*fn)())改成

            void connect( Signal0& sgn,T1& obj, void(T2::*fn)())

            另外加了kDisConnect釋放內(nèi)存,原來只作測試沒寫它,現(xiàn)在還是加上了。

            posted on 2007-09-03 15:22 楊粼波 閱讀(500) 評論(0)  編輯 收藏 引用

            伊人久久无码中文字幕| 999久久久国产精品| 国产福利电影一区二区三区,免费久久久久久久精 | 99久久精品国产一区二区 | 午夜精品久久久久久久无码| 国产香蕉97碰碰久久人人| 情人伊人久久综合亚洲| 伊人久久精品线影院| 一级做a爱片久久毛片| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 青青草国产精品久久久久| 四虎国产精品免费久久久| 婷婷久久综合九色综合98| 久久夜色精品国产亚洲| 国内精品久久久久久麻豆| 精品久久久久中文字幕一区| 久久夜色撩人精品国产小说| 日韩中文久久| 亚洲色欲久久久综合网东京热 | 久久久午夜精品福利内容| 亚洲AV日韩AV天堂久久| 久久91亚洲人成电影网站| 精品人妻伦九区久久AAA片69| 狠狠色丁香婷婷久久综合| 粉嫩小泬无遮挡久久久久久 | 青青久久精品国产免费看| 久久伊人五月丁香狠狠色| 国内精品久久久久影院免费| 久久精品国产清自在天天线| 午夜精品久久久久久中宇| 久久久久久久综合日本亚洲| 欧美激情精品久久久久久| 久久久久久亚洲AV无码专区| 久久国产精品一区| 久久久免费精品re6| 欧美日韩中文字幕久久久不卡 | 久久精品国产欧美日韩| 久久99精品久久久久久久久久| 久久久久国产精品麻豆AR影院| 精品免费久久久久久久| 欧美精品国产综合久久|