亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

使用C++深入研究.NET委托與事件

2019-11-17 05:11:25
字體:
來源:轉載
供稿:網友
簡介

  類型安全機制的實現原來采用的是C風格的回調(callback)函數,而.NET Framework引入了委托和事件來替代原來的方式;它們被廣泛地使用。
我們在這里嘗試使用標準C++來實現與之類似的功能,這樣我們不但可以對這些概念有一個更好的熟悉,而且同時還能夠體驗C++的一些有趣的技術。



  C#中的委托與事件要害字

  首先我們來看一個簡單的C#程序(下面的代碼略有刪節)。執行程序的輸出結果如下顯示:

SimpleDelegateFunction called from Ob1,

string=Event fired!

Event fired!(Ob1): 3:49:46 PM on

Friday, May 10, 2002

Event fired!(Ob1): 1056318417

SimpleDelegateFunction called from Ob2,

string=Event fired!

Event fired!(Ob2): 3:49:46 PM on

Friday, May 10, 2002

Event fired!(Ob2): 1056318417

  所有這些都源于這樣一行代碼:dae.Fire
  在利用C++來實現這些功能時,我模擬了C#的語法并完全按照功能的要求進行開發。

namespace DelegatesAndEvents
{
class DelegatesAndEvents
{
public delegate void PrintString(string s);
public event PrintString MyPrintString;
public void FirePrintString(string s)
{
if (MyPrintString != null)MyPrintString(s);
}
}

class TestDelegatesAndEvents
{
[STAThread]
static void Main(string[] args)
{
DelegatesAndEvents dae =new DelegatesAndEvents();
MyDelegates d = new MyDelegates();
d.Name = "Ob1";
dae.MyPrintString +=new DelegatesAndEvents.PrintString(d.SimpleDelegateFunction);
// ... more code similar to the
// above few lines ...
dae.FirePrintString("Event fired!");
}
}
class MyDelegates
{
// ... "Name" property omitted...
public void SimpleDelegateFunction(string s)
{
Console.WriteLine("SimpleDelegateFunction called from {0}, string={1}", m_name, s);
}
// ... more methods ...
}
} 更多文章 更多內容請看.NET開發手冊  .NET實用開發  .NET移動與嵌入式技術專題,或
C++中的類型安全函數指針

  對于“老式方法”的批判之一便是它們不是類型安全的[1]。下面的代碼證實了這個觀點:

typedef size_t (*FUNC)(const char*);
void printSize(const char* str) {
FUNC f = strlen;
(void) printf("%s is %ld chars/n", str, f(str));
}
void crashAndBurn(const char* str) {
FUNC f = reinterpret_cast<FUNC>(strcat);
f(str);
}
  代碼在[2]中可以找到。當然,在你使用reinterpret_cast的時候,你可能會碰到麻煩。假如你將強制轉換(cast)去掉,C++編譯器將報錯,而相對來說更為安全的static_cast也不能夠完成轉換。這個例子也有點像比較蘋果和橙子,因為在C#中萬事萬物皆對象,而reinterpret_cast就相當于一種解決方式。下面的這個C++程序示例將會采取使用成員函數指針的方法來避免使用reinterpret_cast:


strUCt Object { };
struct Str : public Object {
size_t Len(const char* str) {
return strlen(str);
}

char* Cat(char* s1, const char* s2) {
return strcat(s1, s2);
}
};

typedef size_t (Object::*FUNC)(const char*);
void printSize(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars/n", s, (str.*f)(s));
}

void crashAndBurn(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Cat);
(str.*f)(s);
}
  static_cast運算符將轉化Str::Len函數指針,因為Str是由Object派生來的,但是Str::Cat是類型安全的,它不能被轉換,因為函數簽名是不匹配的。

  成員函數指針的工作機制與常規的函數指針是非常相似的;唯一不同(除了更為復雜的語法外)的是你需要一個用來調用成員函數的類的實例。當然,我們也可以使用->*運算符來用指向類實例的指針完成對成員函數的調用。

Str* pStr = new Str();
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars/n", s, (str->*f)(s));
delete pStr;
  只要所有的類是從基類Object派生來的(C#中就是這樣),你就可以使用C++來創建類型安全的成員函數指針。 更多文章 更多內容請看.NET開發手冊  .NET實用開發  .NET移動與嵌入式技術專題,或
創建一個委托類

  擁有類型安全成員函數指針是我們效仿.NET功能的第一部。盡管如此,單獨的成員函數指針是毫無用處的 — 你總是需要一個類的實例;委托對象同時保持在兩邊,使得調用成員函數非常方便。我們接著上面的例子續寫下面的代碼:

struct StrLen_Delegate
{
typedef size_t (Str::*MF_T)(const char*);
MF_T m_method;
Object& m_pTarget;
StrLen_Delegate(Object& o, const MF_T& mf) :
m_pTarget(&o), m_method(mf) {}

MF_T Method() const {
return m_method;
}

Object& Target() const {
return *m_pTarget;
}

size_t Invoke(const char* s) {
(m_pTarget.*m_method)(s);
}
};

void printSize2(const char* s) {
Str str;
StrLen_Delegate d(str, &Str::Len);
(void) printf("%s is %ld chars/n", s,
d.Invoke(s));
}
  有了委托類,調用成員函數變得更為簡單。使用運算符代替Invoke來給這個類創建一個仿函數將使調用降為僅有d(s);為了清楚以及和.NET規定匹配,我使用Invoke。需要注重的是,類的實例是一個對象(Object)而不是Str。只要簽名匹配,從Object派生來的任何一個類的成員函數指針將答應被用于創建委托。

  這個類在這個例子中使用能夠工作得非常好,但是它不是非常靈活;我們必須為每一個可能的成員函數簽名寫一個新的委托類。.NET使用由公用語言運行時(Common Language Runtime)維護的rich type信息來解決這個問題。但這在C++中不是一個非??尚械霓k法,但是可以采用
模板來完成類似的功能。我們不用將Invoke函數的參數設為const char* s,而是將類型指定為模板參數:

template <typename ARG1>
struct StrLen_Delegate
{
typedef size_t (Str::*MF_T)(ARG1);
// ... as above ...
size_t Invoke(ARG1 v1) {
(m_pTarget.*m_method)(v1);
}
};
  這樣效果就好很多了,但是Invoke函數將只作用于單參數的成員函數。并且,委托也僅僅關心類的實例以及成員函數指針;它不是真正關心成員函數指針的細節。最后,我們很方便地就能夠為成員函數指針產生一個typedef作為模版參數使用。由于一切都是由Object類派生出來的,這些細節也可以被移動到Object當中:

struct Object
{
template <typename ARG1>
struct void1_T {
typedef void (Object::*mf_t)(ARG1);
};

template <typename ARG1, typename ARG2>
void Invoke(void1_T<ARG1>::mf_t mf, ARG1 v1, ARG2) const {
(this->*mf)(v1);
}
};

template <typename CLASS>
class ObjectT : public Object {};
typedef ObjectT<void> VoidType;
  這個Object基類包含了一個typedef對應每一個成員函數簽名;我使用了void返回類型來簡化了很多需要做的工作。Typedef可以參照如下方式使用:

typedef Object::void1_T<std::string>::mf_t StringMF_t;

  我們使用了std::string類型的參數和void返回類型就能夠非常輕易地為成員函數指針創建typedef。

  程序根據附加的參數對于Invoke是跟蹤計數的。這是非常必要的,因為對于所有的Invoke方法必須有同樣數目的參數;重載決策基于第一個參數—成員函數指針的類型,來完成。需要注重的是大部分的.NET Framework將在委托中使用EventArgs對象來避免上述的復雜情況。你可以通過從EventArgs派生來添加額外的參數而不需要給委托添加簽名。

  最后,ObjectT模版提供了一個簡單的方法用來產生唯一類型,每一個類型最終是從Object派生來的。這就確保了類型安全。

  基于上面所有的內容,委托類現在就應當是如下所示的樣子:


template <typename MF_T>

class DelegateT_ : public ObjectT<MF_T>
{
MF_T m_method;
Object* m_pTarget;
protected:
DelegateT_() : m_pTarget(NULL), m_method(NULL) {}
DelegateT_(Object& o, const MF_T& mf) :
m_pTarget(&o), m_method(mf) {}
public:
MF_T Method() const {
return m_method;
}
Object& Target() const {
return *m_pTarget;
}
};
  模板參數現在就是一個typedef成員函數指針(生成方法如上所示),而Invoke方法繼續于Object基類。 更多文章 更多內容請看.NET開發手冊  .NET實用開發  .NET移動與嵌入式技術專題,或 維護委托集

  在C#中,Delegate和Event要害字成對出現用來創建一列委托,就像上面的第一個例子:

new DelegatesAndEvents.PrintString(d.SimpleDelegateFunction);

  創建一個新的類似于我的C++實現的委托對象:

StrLen_Delegate d(str, &Str::Len);

  MyPrintString對象是一個擁有重載運算符+=的事件,這是用來添加委托的。在C++中我們也可以模擬這個功能來完成類似的工作。C#中的Delegate要害字創建了一個MultiCastDelegate對象(詳見[3])。你會注重到我將上面的委托類命名為DelegateT_(尾隨的下劃線說明這個名字是保留的)。嚴格地說,名字_DelegateT是為這個程序實現而保留的(__DelegateT也是一樣的)因為下劃線后跟隨著一個大寫字母。_delegateT也可以(僅有一個被小寫字母尾隨其后的下劃線),但是我偏向于避免所有的由于前下劃線所可能導致的潛在錯誤(閱讀我寫的代碼的人很可能抓不到我的所有規則)也不愿意采用后劃線代替它。保留DelegateT_是因為完成效仿.NET功能的委托類是從多播委托(MultiCastDelegate)類派生來的。

  Delegate對象可以很輕易地被存儲在標準C++容器中。我將使用list,因為它與.NET的工作機制是最接近的。依據你個人的需要,也可以使用vector或者deque。使用集(set)來提供不論委托被附加入幾次,僅僅調用一次的有趣的特性。MultiCastDelegate的第一部分如下所示:

template <typename MF_T, typename ARG1 = VoidType,
typename ARG2 = VoidType>
class MulticastDelegateT : public DelegateT_<MF_T>
{
typedef DelegateT_<MF_T> Delegate;
typedef std::list<Delegate> Delegates_t;
protected:
MulticastDelegateT() {}
public:
MulticastDelegateT(Object& o, const MF_T& mf) :
Delegate(o, mf) {}

MulticastDelegateT& Operator+=(const Delegate& d) {
m_delegates.push_back(d);
return *this;
}
private:
Delegates_t m_delegates;
};
  這里使用了list和幾個typedef來存儲委托集。它需要從DelegateT_派生而來,因為下面我將從MultiCastDelegateT派生出DelegateT作為真正的委托類。

  而后激發所有被存儲的委托上的一個C#循環中的事件并調用每一個。因為我使用的是標準容器,使迭代器將很方便:

void operator()(ARG1 v1 = VoidType(),
ARG2 v2 = VoidType()) const {
for (Delegates_t::const_iterator it = m_delegates.begin();
it != m_delegates.end(); ++it)
(it->Target()).Invoke(it->Method(), v1, v2);
}
  即使你很適應標準C++容器,這可能也是你不熟悉的一行代碼:只在一個模版類中就可以使用迭代器調用成員函數!對迭代器取反引用,我們可以清楚地看到發生了什么:


const Delegate& d = *it;
d.Invoke(d.Method(), v1, v2);
  假如你對迭代器還不是很適應,你可以指出一個就像數組一樣的deque:

for (int i=0; i<m_delegates.size(); i++)
Delegate d = m_delegates[i];
  在這里,你可以為DelegateT_ 類添加下面的模板成員函數:

template <typename ARG1, typename ARG2>
void Invoke_(ARG1 v1 = ARG1(), ARG2 v2 = ARG2()) const {
this->Invoke(m_method, v1, v2);
}
  這樣就避免了MultiCastDelegateT::Invoke方法一定要將成員函數指針傳遞給Object::Invoke:

d.Invoke_(v1, v2);

  盡管如此,這將需要每一個參數都有一個默認構造函數,但事實卻不見得如此。并且,由于MultiCastDelegateT是真正的委托基類,看上去并沒有太大的必要調用Object::Invoke 路徑—即使由于這個原因代碼顯得更為復雜。(這也會在Visual C++.NET中導致可怕的“內部編譯器錯誤”)。

  實際的委托類現在僅僅是MultiCastDelegateT的一個簡單的包裝:

template <typename MF_T, typename ARG1 = VoidType,
typename ARG2 = VoidType>
struct DelegateT :
public MulticastDelegateT<MF_T, ARG1, ARG2>
{
DelegateT(Object& o, const MF_T& mf) :
MulticastDelegateT<MF_T, ARG1, ARG2>(o, mf) {}
DelegateT() {}
typedef DelegateT<MF_T, ARG1, ARG2> Event;
};
  它的主要功能是提供事件typedef。

  將他們集成起來

  現在你可以用C++編寫實現C#例子當中的DelegatesAndEvents類了:

class DelegatesAndEvents
{
// C#: public delegate void PrintString(string s);
typedef DelegateT<Object::void1_T<std::string>::mf_t,
std::string> PrintString_;
public:
template <typename OBJECT>
static PrintString_ PrintString(OBJECT& o,
void (OBJECT::*mf)(std::string)) {
return PrintString_(o,
static_cast<Object::void1_T<std::string>::mf_t>(mf));
}

// C#: public event PrintString MyPrintString;

PrintString_::Event MyPrintString;
void FirePrintString(std::string s) {
MyPrintString(s);
}
};
  這樣的語法看上去著實令人恐怖,假如你愿意,可以用一些靈巧的宏來簡化它。但最近宏的名聲不太好,并且我們進行的這個主題要害是要了解細節。無論怎樣,你都應當感謝C#編譯器為你做的工作。

  第一行代碼創建一個成員函數指針私有的typedef,名稱為PrintString_。參數類型std::string需要列兩次,這太糟了,但是這正是由于Visual C++不支持局部模版特化造成的。static方法為創建你自己的類型的委托提供了一個方便的方法,答應你這樣來寫你的代碼:

DelegatesAndEvents::PrintString_
myDelegate = DelegatesAndEvents::PrintString(d,&MyDelegates::SimpleDelegateFunction);

  這與上面的C#代碼是類似的。

  而后,我們使用來自DelegateT_的Event typedef創建事件。請注重這一系列的typedef是如何答應C++代碼至少是有C#代碼一些類似之處的。最后,有一個方法觸發事件,這與C#尤其相同。(由于你采用的是標準容器,所以不必擔心NULL列表。)

  使用委托和事件的客戶端的代碼就很明了了,而且也很類似于C#代碼(同樣這些代碼也是略有縮減的):

struct MyDelegates : public ObjectT<MyDelegates>
{
// ... Name omitted...
void SimpleDelegateFunction(std::string s)
{
printf("SimpleDelegateFunction called from %s,
string=%s/n", m_name.c_str(), s.c_str());
}

// ... more methods ...
};

void CppStyle()
{
DelegatesAndEvents dae;
MyDelegates d;
d.Name() = "Obj1";
dae.MyPrintString += DelegatesAndEvents::PrintString
(d, &MyDelegates::SimpleDelegateFunction);
// ... more code similar to the above few lines ...
dae.FirePrintString("Event fired!");
}
  請注重MultiCastDelegateT::operator+=是如何被調用來為委托列表添加每一個由靜態方法DelegatesAndEvents::PrintString返回的委托的。 更多文章 更多內容請看.NET開發手冊  .NET實用開發  .NET移動與嵌入式技術專題,或

托管C++

  由于委托和事件是.NET框架的一部分,所有的.NET支持的語言都可以使用它們。我所描述的基于模版的實現是專門針對C++的。Microsoft采用了不同的方法在C++中將這個功能公開—對于標準C++的擴展稱為托管C++。也許你并不感到太吃驚,在托管C++中編寫這個例子與最初的代碼是那么相似:

public __gc struct DelegatesAndEvents {
__event void MyPrintString(String* s);
void FirePrintString(String* s) {
MyPrintString(s);
}
};

__gc struct MyDelegates
{
String* Name;
void SimpleDelegateFunction(String* s) {
Console::WriteLine
("SimpleDelegateFunction called from {0} string={1}",Name, s);
}
};

void ManagedCpp()
{
DelegatesAndEvents* dae = new DelegatesAndEvents();
MyDelegates* d = new MyDelegates();
d->Name = "Obj1";
__hook(&DelegatesAndEvents::MyPrintString, dae,
&MyDelegates::SimpleDelegateFunction, d);
dae->FirePrintString(S"Event fired!");
}
  要害字__gc標志著這個類是被垃圾回收機制控制的(托管的);我們不需要調用delete函數。僅僅一個__event要害字就完成了我們上面代碼的大部分功能。需要注重的是托管C++使用__hook要害字來替代上面討論的操作符+=。你會發覺使用-Fx標記[4]調用(托管)C++編譯器編譯上述代碼和檢查產生的結果文件.mrg非常有趣。在編譯器級加入新功能而不是編寫模板顯然要輕易得多了。

  結論

  通過使用極為高級的C++技巧,我已經向大家展示了用C++為簡單的樣例代碼實現委托與事件是可行的。這個實現主要考慮基于.NET框架。更為一流和純粹的C++解決方案可以使用C++標準庫中的適配器和聯編程序。

  參考文獻

  [1] Jeffrey Richter. “An Introduction to Delegates,” MSDN Magazine, April 2001.

< http://msdn.microsoft.com/msdnmag/issues/01/04/net/default.aspx >.

  [2] Richard Grimes. “.NET Delegates: Making Asynchronous Method Calls in the .NET Environment,” MSDN Magazine, August 2001.

<http://msdn.microsoft.com/msdnmag/issues/01/08/Async/default.aspx>.

  [3] Jeffrey Richter. “Delegates, Part 2,” MSDN Magazine, June 2001.

< http://msdn.microsoft.com/msdnmag/issues/01/06/net/default.aspx>

  [4] Bobby Schmidt. “The Red Pill,” April 23, 2002.

<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndeepc/Html/deep04232002.asp >

  譯者注

  譯注1:Type-safe:按照2003年微軟官方提供的術語表翻譯為“類型安全”。

  譯注2:overload resolution: 按照2003年微軟官方提供的術語表翻譯為“重載決策”。

  譯注3:原文中所列參考文獻的地址已經失效,譯文中提供的是在本文翻譯截稿時所示參考的最新有效鏈接,為尊重原著者特此說明。

  譯注4:destructor一詞按照簡體中文常用譯法譯為“反引用”。

  譯注5:關于文中采用的reinterpret_cast。事實上,reinterpret_cast在這里是通不過的。因為我們不可能對成員函數指針進行所謂的類型轉換。這個例子實際上是在比較對象,轉換的也是對象,而不是對象的成員。而這個示例卻將reinterpret_cast作為解決的方式,即直接比較的是對象的成員,而不考慮對象。也就是說,試圖轉換對象的成員。而失去類型轉換的真正意圖。為什么作者在這里用了reinterpret_cast,意為“重新意義上的強制轉換“。這種轉換并不是基于類型或者是對象的,更談不上類型安全了。委托的本質上講是函數指針,不過,它需要首先進行類型檢查。我們說委托對象的存在,只是為了類型檢查,真正有意義的還是其方法。所以reinterpret_cast相當于一種解決方式。 更多文章 更多內容請看.NET開發手冊 
 .NET實用開發  .NET移動與嵌入式技術專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲最新中文字幕| 欧美电影在线播放| 久久久久国产一区二区三区| 成人啪啪免费看| 最近中文字幕2019免费| 国产美女精品视频免费观看| 国产精品国产三级国产专播精品人| 7777精品久久久久久| 欧美国产日本高清在线| 色诱女教师一区二区三区| 国产丝袜一区二区三区| 亚洲精品视频二区| 91在线免费看网站| 久久精品国产清自在天天线| 亚洲最新av在线网站| 国产一区二区在线免费| 午夜精品久久久久久99热| 国产91精品不卡视频| 欧美日韩在线视频首页| 一本一本久久a久久精品综合小说| 日本高清视频一区| 国产欧美一区二区白浆黑人| 国产精品青草久久久久福利99| 日韩av快播网址| 亚洲深夜福利在线| 亚洲一区www| 国产自产女人91一区在线观看| 国内自拍欧美激情| 久久亚洲精品一区二区| 欧美久久精品一级黑人c片| 国产精品黄色av| 日韩成人在线视频| 国产精品视频一区国模私拍| 日韩成人免费视频| 国产精品亚洲综合天堂夜夜| 亚洲大胆人体av| 国产精品第三页| 国产精品青青在线观看爽香蕉| 欧美巨猛xxxx猛交黑人97人| 国产综合福利在线| 成人在线视频福利| 日韩欧美在线观看| 国产精品黄色影片导航在线观看| 91美女高潮出水| 一区二区在线视频播放| 庆余年2免费日韩剧观看大牛| 日韩欧美亚洲国产一区| 欧美一区二区.| 亚洲成人av资源网| 国产日产亚洲精品| 国模吧一区二区三区| 欧美日韩加勒比精品一区| 国产午夜精品免费一区二区三区| 欧美又大粗又爽又黄大片视频| 日韩av免费在线观看| 亚洲第一福利网站| 日韩在线视频免费观看高清中文| 久久久久久久久网站| 91av在线精品| 91精品视频一区| 日韩美女在线看| 啪一啪鲁一鲁2019在线视频| 久久久久久国产精品三级玉女聊斋| 欧洲永久精品大片ww免费漫画| 国产精品亚洲美女av网站| 国产精品电影一区| 国产精品日韩欧美大师| 精品一区二区三区电影| 91sao在线观看国产| 亚洲美女久久久| 欧美视频在线免费| 精品福利一区二区| 亚洲男人天堂网站| 91精品国产乱码久久久久久蜜臀| 国产精品av在线| 亚洲欧美一区二区精品久久久| 欧美日韩精品在线| 国产精品香蕉国产| 亚洲精品美女免费| 精品久久久视频| 日韩欧美在线免费| 中文亚洲视频在线| 日韩成人久久久| 亚洲欧美日韩天堂一区二区| 国产精品成人免费视频| 亚洲高清一区二| 大伊人狠狠躁夜夜躁av一区| 亚洲男人的天堂网站| 亚洲香蕉伊综合在人在线视看| 亚洲精品在线91| 亚洲综合av影视| 2019中文字幕在线| 国产精品爽爽爽爽爽爽在线观看| 91性高湖久久久久久久久_久久99| 久久艳片www.17c.com| 中文字幕亚洲字幕| 色无极影院亚洲| 亚洲成人激情小说| 久久的精品视频| 亚洲天堂精品在线| 久久精品一偷一偷国产| 国产精品久久久久久久久久ktv| 国产剧情日韩欧美| 国产精品久久久久久超碰| 国产亚洲一区二区在线| 久久精品国产免费观看| 狠狠躁夜夜躁久久躁别揉| 国产精品高精视频免费| 国产日韩精品在线| 日韩精品视频免费专区在线播放| 欧美在线视频网| 久久久免费观看视频| 丁香五六月婷婷久久激情| 国产精品视频网站| 亚洲欧美国产精品| …久久精品99久久香蕉国产| 91久久夜色精品国产网站| 国产精品99久久久久久久久久久久| 成人午夜高潮视频| 欧美在线视频免费| 日韩av在线资源| 欧美成人黄色小视频| 中文字幕日韩av电影| 高清欧美一区二区三区| 伊人男人综合视频网| 欧美日韩午夜视频在线观看| 亚洲一区二区三区视频| 国产噜噜噜噜久久久久久久久| 精品国产一区二区三区久久狼黑人| 久久久国产精品亚洲一区| 播播国产欧美激情| 亚洲欧美日韩爽爽影院| 色婷婷成人综合| 97热在线精品视频在线观看| 国产一区二区三区在线视频| 精品国产福利在线| 91视频九色网站| 国产91精品久| 91po在线观看91精品国产性色| 成人av在线网址| 亚洲精品720p| 狠狠躁夜夜躁人人躁婷婷91| 久久精品国产综合| 欧美国产第一页| 国产成人亚洲综合青青| 中国china体内裑精亚洲片| 日韩最新在线视频| 欧美中文字幕视频在线观看| 国产精品白嫩初高中害羞小美女| 国产成人免费91av在线| 亚洲а∨天堂久久精品喷水| 久久激情五月丁香伊人| 欧美视频在线看| 久久精品国产精品亚洲| 国产精品欧美一区二区| 欧美黑人巨大xxx极品| 国产一区二区三区在线视频| 久久视频在线播放| 国产一区二区成人| 日韩美女av在线| 欧美成人h版在线观看| 日韩视频精品在线| 在线观看精品自拍私拍| 成人黄色免费看|