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

洛譯小筑

別來無恙,我的老友…
隨筆 - 45, 文章 - 0, 評(píng)論 - 172, 引用 - 0
數(shù)據(jù)加載中……

[ECPP讀書筆記 條目35] 虛函數(shù)的替代方案

假設(shè)你正在設(shè)計(jì)一款游戲軟件,你需要為游戲中的各種角色設(shè)計(jì)一個(gè)層次結(jié)構(gòu)。你游戲的劇情中充滿了風(fēng)險(xiǎn)刺激,那么游戲中的角色就會(huì)經(jīng)常遇到受傷或者生命值降低的情況。于是你決定為角色類提供一個(gè)成員函數(shù):healthValue,這一函數(shù)通過返回一個(gè)整數(shù)值來表示角色當(dāng)前的生命值。由于計(jì)算不同角色生命值的方式可能會(huì)不一樣,因此不妨將healthValue聲明為虛函數(shù),這樣做十分順理成章:

class GameCharacter {

public:

  virtual int healthValue() const; // 返回角色的生命值

  ...                              // 派生類可以重定義該函數(shù)

};

此處,healthValue函數(shù)并沒有直接聲明為純虛函數(shù),這一事實(shí)告訴我們計(jì)算生命值存在著一種默認(rèn)的算法(參見條目34)。

事實(shí)上,所謂“順理成章”的方法,在某些情況下卻會(huì)成為工作的絆腳石。由于這一設(shè)計(jì)太過“順理成章”了,你也許就不會(huì)再為尋找替代方案花費(fèi)足夠的精力。為了讓你從面向?qū)ο笤O(shè)計(jì)的思維定勢(shì)中解脫出來,讓我們另辟蹊徑,解決這一問題。

模板方法模式:通過“非虛接口慣用法”實(shí)現(xiàn)

我們從討論一個(gè)有趣的思想流派開始,這一流派堅(jiān)持虛函數(shù)必定為私有函數(shù)。其追捧者也許會(huì)提出,更優(yōu)秀的方案中healthValue仍為公共成員函數(shù),但將其聲明為非虛函數(shù),并讓其調(diào)用一個(gè)私有的虛函數(shù),真正的工作由這個(gè)虛函數(shù)來完成。不妨將其命名為doHealthValue

class GameCharacter {

public:

  int healthValue() const            // 派生類不能對(duì)其進(jìn)行重定義

  {                                   // 參見條目36

    ...                               // 準(zhǔn)備工作:參見下文

    int retVal = doHealthValue();     // 進(jìn)行實(shí)際的工作

    ...                               // 后期工作:參見下文 

    return retVal;

  }

  ...

 

private:

  virtual int doHealthValue() const  // 派生類可以對(duì)其進(jìn)行重定義

  {

    ...                               // 計(jì)算角色生命值的默認(rèn)算法

  }

};

上文的代碼中(以及本條目中所有其它代碼中),我將成員函數(shù)的定義內(nèi)容放置在了類定義中。如同條目30中所解釋的,這樣做使得這些函數(shù)隱式帶有內(nèi)聯(lián)屬性。我這樣做只是為了讓所說明的問題更加簡單明了。是否選擇內(nèi)聯(lián)與本條款的設(shè)計(jì)方案無關(guān),因此不要認(rèn)為這里在類的內(nèi)部編寫成員函數(shù)是出于什么目的的。不要誤會(huì)了。

讓客戶通過調(diào)用公有的非虛成員函數(shù)來間接的調(diào)用私有虛函數(shù)——這是一個(gè)基本的設(shè)計(jì)方案。我們一般將其稱為“非虛擬接口慣用法(non-virtual interface,簡寫為NVI)” 。這一方案是一個(gè)更為一般化的設(shè)計(jì)模式“模板方法”(可惜這一模式和C++中的模板并沒有什么關(guān)系)的一個(gè)特定的表現(xiàn)形式。我們將這些非虛函數(shù)(比如上文中的healthValue)稱為虛函數(shù)的“包裝器”。

請(qǐng)留意上文代碼中的“準(zhǔn)備工作”、“后期工作”這兩段注釋內(nèi)容。在虛函數(shù)做實(shí)際工作的前后,這兩段注釋處的代碼肯定會(huì)得到調(diào)用。這是NVI慣用法的一個(gè)優(yōu)勢(shì),這樣做意味著在程序調(diào)用虛函數(shù)之前,相關(guān)的背景工作已經(jīng)設(shè)定好了;在調(diào)用虛函數(shù)以后,后臺(tái)清理工作也能得以進(jìn)行,這兩項(xiàng)工作都是由包裝器確保進(jìn)行的。舉例說,“準(zhǔn)備工作”可能包含互斥鎖加鎖、日志記錄、確認(rèn)當(dāng)前類的約束條件和函數(shù)的先決條件是否滿足,等等。“后期工作”可能包括互斥鎖解鎖、檢查函數(shù)的后置條件、重新檢查類的約束條件,等等。如果你讓客戶直接調(diào)用虛函數(shù)的話,那么這些工作也就很難開展了。

你也許注意到了:在使用NVI慣用法時(shí),我們可以在派生類中對(duì)私有虛函數(shù)進(jìn)行重定義,而這些函數(shù)在派生類中是無法調(diào)用的?。∵@一點(diǎn)看上去匪夷所思,但實(shí)際上這里并不存在設(shè)計(jì)上的矛盾。虛函數(shù)的重定義工作確認(rèn)了它實(shí)現(xiàn)其功能的方式,虛函數(shù)的調(diào)用工作確認(rèn)了它實(shí)現(xiàn)其功能的時(shí)機(jī)。兩者是相互獨(dú)立的:NVI慣用法允許在派生類中重定義虛函數(shù),這樣做使得基類將虛函數(shù)調(diào)用方式的選擇權(quán)賦予派生類,然而基類仍保留函數(shù)調(diào)用時(shí)機(jī)的決定權(quán)。這一點(diǎn)乍看上去有些異樣,但是,“允許派生類對(duì)私有虛函數(shù)進(jìn)行重定義”這一C++規(guī)則是非常合理的。

在NVI慣用法的背景下,并沒有嚴(yán)格要求虛函數(shù)必須是私有的。在一些類層次結(jié)構(gòu)中,一個(gè)虛函數(shù)可能在某個(gè)派生類中的實(shí)現(xiàn)版本中調(diào)用基類的其他成員(參見條目27中SpecialWindow的例子),為了讓這樣的調(diào)用合法,這個(gè)虛函數(shù)必須聲明為受保護(hù)的,而不是私有的。還有一些情況下虛函數(shù)甚至要聲明為公有的(參見條目7:多態(tài)基類中的析構(gòu)函數(shù)),在這些情況下NVI慣用法不再可用。

策略模式:通過函數(shù)指針實(shí)現(xiàn)

NVI慣用法是公有虛函數(shù)的一個(gè)有趣的替代方案,但是從設(shè)計(jì)的觀點(diǎn)來看,這樣做無異于粉飾門面罷了。畢竟我們?nèi)匀辉谑褂锰摵瘮?shù)來計(jì)算每一個(gè)角色的生命值。這里存在著一個(gè)改進(jìn)效果更為顯著的設(shè)計(jì)主張:計(jì)算角色生命值的工作應(yīng)與角色類型完全無關(guān),于是計(jì)算生命值的工作便無須為角色類的成員。舉例說,我們可以讓每個(gè)角色類的構(gòu)造函數(shù)包含一個(gè)函數(shù)指針參數(shù),并由這一參數(shù)將生命值計(jì)算方法函數(shù)傳入,我們可以通過調(diào)用這個(gè)函數(shù)進(jìn)行實(shí)際的計(jì)算工作:

class GameCharacter;               // 前置聲明

 

// defaultHealthCalc函數(shù):計(jì)算生命值的默認(rèn)算法

int defaultHealthCalc(const GameCharacter& gc);

 

class GameCharacter {

public:

  typedef int (*HealthCalcFunc)(const GameCharacter&);

 

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)

  : healthFunc(hcf)

  {}

 

  int healthValue() const

  { return healthFunc(*this); }

  ...

private:

  HealthCalcFunc healthFunc;

}; 

這一方案是對(duì)另一個(gè)常見的設(shè)計(jì)模式——“策略模式”的一個(gè)簡單應(yīng)用。與使用虛函數(shù)的GameCharacter層次結(jié)構(gòu)解決方案相比而言,本方案為我們帶來了一些有趣的靈活性:

同一角色類型的不同實(shí)例可以使用不同的生命值計(jì)算函數(shù)。比如:

class EvilBadGuy: public GameCharacter {

public:

  explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)

  : GameCharacter(hcf)

  { ... }

  ...

};

int loseHealthQuickly(const GameCharacter&);  // 不同計(jì)算方式的

int loseHealthSlowly(const GameCharacter&);   // 生命值計(jì)算函數(shù)

 

EvilBadGuy ebg1(loseHealthQuickly);           // 同一類型的角色

EvilBadGuy ebg2(loseHealthSlowly);            // 使用不同類型的

                                              // 生命值計(jì)算方法

對(duì)特定的角色的生命值計(jì)算函數(shù)可以在運(yùn)行時(shí)更改。比如說,GameCharacter可以提供一個(gè)名為setHealthCalculator的成員函數(shù),我們可以使用這個(gè)函數(shù)來更換當(dāng)前的生命值計(jì)算函數(shù)。

另一方面,我們發(fā)現(xiàn),生命值計(jì)算函數(shù)不再是GameCharacter層次結(jié)構(gòu)的成員函數(shù)了,這一事實(shí)說明,這類生命值計(jì)算函數(shù)不再有訪問它們所處理對(duì)象的內(nèi)部成員的特權(quán)。比如說,defaultHealthCalc函數(shù)對(duì)于EvilBadGuy類中的非公有成員就沒有訪問權(quán)限。如果角色的生命值可以單純依靠角色類的公有接口所得到的信息來計(jì)算的話,上述的情況并不是問題,但是一旦精確的生命值計(jì)算需要調(diào)用非公有的信息,那么這一情況便成為問題了。事實(shí)上,凡是你使用類外部的等價(jià)功能(比如通過非成員非友元函數(shù)或通過其它類的非友元的成員函數(shù))來替代類內(nèi)部的功能(比如通過成員函數(shù))時(shí),都存在潛在的問題。本篇余下部分內(nèi)容都存在這一問題,因?yàn)槲覀円紤]的其它設(shè)計(jì)方案都會(huì)涉及到GameCharacter層次結(jié)構(gòu)外部函數(shù)的使用。

讓非成員函數(shù)訪問類內(nèi)部的非公有成員只有一個(gè)解決辦法,那就是降低類的封裝度,這是一個(gè)一般的規(guī)則。比如說,類可以將非成員函數(shù)生命為友元,類也可以為需要隱藏的具體實(shí)現(xiàn)提供公有訪問函數(shù)。使用函數(shù)指針代替虛函數(shù)可以帶來一些優(yōu)勢(shì)(比如可以為每一個(gè)角色的實(shí)例提供一個(gè)生命值計(jì)算函數(shù),可以在運(yùn)行時(shí)更換計(jì)算函數(shù)),而這樣做也會(huì)降低GameCharacter的封裝度,至于優(yōu)缺點(diǎn)之間孰輕孰重,你在實(shí)戰(zhàn)中必須做到具體問題具體分析,盡可能的審時(shí)度勢(shì)。

策略模式:通過tr1::function實(shí)現(xiàn)

如果你對(duì)模板和模板的隱式接口的用法(參見條目41)很熟悉的話,那么上述的“基于函數(shù)指針”的方案就顯得十分蹩腳了。我們考慮:舊的方案必須使用一個(gè)函數(shù)來實(shí)現(xiàn)生命值計(jì)算的功能,能不能用其它一些東西(比如函數(shù)對(duì)象)來代替這個(gè)函數(shù)呢?為什么這個(gè)函數(shù)不能是成員函數(shù)呢?還有,為什么這個(gè)函數(shù)一定要返回一個(gè)整數(shù)值,而不能返回一個(gè)可以轉(zhuǎn)換成int類型的其他對(duì)象呢?

如果我們使用一個(gè)tr1::function類型的對(duì)象來代替函數(shù)指針,那么上述問題中的約束則會(huì)瞬間瓦解。就像條目54中所解釋的,tr1::function對(duì)象可以保存任意可調(diào)用實(shí)體(也就是:函數(shù)指針、函數(shù)對(duì)象、或成員函數(shù)指針),這些實(shí)體的簽名一定與所期待內(nèi)容的類型兼容。以下是我們剛剛看到的設(shè)計(jì)方案,這次加入了tr1::function的使用:

class GameCharacter;               // 同上

int defaultHealthCalc(const GameCharacter& gc);

                                   // 同上

class GameCharacter {

public:

// HealthCalcFunc可以是任意可調(diào)用的實(shí)體,

// 它可以被任意與GameCharacter類型兼容的對(duì)象調(diào)用

// 且返回值必與int類型兼容,詳情見下文

   typedef std::tr1::function<int (const GameCharacter&)>

             HealthCalcFunc;

   explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)

   : healthFunc(hcf)

   {}

 

   int healthValue() const

   { return healthFunc(*this); }

 

   ...

 

private:

  HealthCalcFunc healthFunc;

};

正如你所見到的,HealthCalcFunc是對(duì)tr1::function某個(gè)實(shí)例的一個(gè)typedef。這意味著它與一般的函數(shù)指針類型并無二致。請(qǐng)仔細(xì)觀察,HealthCalcFunc所表示的自定義類型:

std::tr1::function<int(const GameCharacter&)>

在這里,我對(duì)此tr1::function實(shí)例“目標(biāo)簽名”做加粗處理。這里目標(biāo)簽名為“一個(gè)包含const GameCharacter&參數(shù)并返回int類型的函數(shù)”。此tr1::function類型(也就是HealthCalcFunc類型)的對(duì)象可以保存兼容此目標(biāo)簽名的任意可調(diào)用實(shí)體。“兼容”意味著實(shí)體中必須包含(或可隱式轉(zhuǎn)換為)const GameCharacter&類型的參數(shù),并且此實(shí)體必須返回一個(gè)(或可隱式轉(zhuǎn)換為)int類型的返回值。

與上文中我們剛剛看到過的設(shè)計(jì)方案(GameCharacter保存一個(gè)函數(shù)指針)相比,本方案并沒有顯著的改動(dòng)。二者僅有的差別就是后一版本中GameCharacter可以保存tr1::function對(duì)象——指向函數(shù)的一般化指針。這一改變實(shí)際上是十分微不足道的,甚至有些跑題,但是,它顯著提高了客戶在程序中修改角色的生命值計(jì)算函數(shù)的靈活性,這一點(diǎn)還是沒有偏離議題的:

short calcHealth(const GameCharacter&);      // 生命值計(jì)算函數(shù)

                                              // 注意:返回值類型非int

 

struct HealthCalculator {                    // 生命值計(jì)算類

  int operator()(const GameCharacter&) const // 函數(shù)對(duì)象

  { ... }

};

 

class GameLevel {

public:

  float health(const GameCharacter&) const;  // 生命值計(jì)算成員函數(shù)

  ...                                         // 注意:返回類型非int

};

 

class EvilBadGuy: public GameCharacter {     // 同上

  ...

};

class EyeCandyCharacter: public GameCharacter {

  ...                                         // 另一個(gè)角色類型;

};                                            // 假設(shè)與EvilBadGuy

                                              // 有同樣的構(gòu)造函數(shù)

 

EvilBadGuy ebg1(calcHealth);                 // 使用函數(shù)的角色

 

EyeCandyCharacter ecc1(HealthCalculator());  // 使用函數(shù)對(duì)象的角色

 

GameLevel currentLevel;

...

EvilBadGuy ebg2( std::tr1::bind(&GameLevel::health, currentLevel, _1) );

                                              // 使用成員函數(shù)的角色

                                              // 詳情見下文

我個(gè)人認(rèn)為,tr1::function可以把你帶入一個(gè)全新的美妙境界,它讓我“熱血沸騰”。如果你的熱血暫時(shí)沒有那么“沸騰”,那可能是因?yàn)槟氵€糾結(jié)于ebg2的定義,你還不了解tr1::bind的工作機(jī)理。請(qǐng)?jiān)试S我對(duì)它做出介紹。

我要說的是,要計(jì)算ebg2的生命值,需要使用GameLevel類中的health成員函數(shù)?,F(xiàn)在,我們所聲明的GameLevel::health函數(shù)表面上看包含一個(gè)參數(shù)(一個(gè)指向GameCharacter對(duì)象的引用),但是它實(shí)際上包含兩個(gè)參數(shù),這是因?yàn)樗€包含一個(gè)隱式的GameLevel參數(shù)(this指針?biāo)笇?duì)象)。然而,GameCharacter類的生命值計(jì)算函數(shù)僅包含一個(gè)參數(shù)(需要計(jì)算生命值的GameCharacter對(duì)象)。如果我們使用GameLevel::health函數(shù)來計(jì)算ebg2的生命值的話,為了“適應(yīng)”它,我們這里只能使用一個(gè)參數(shù)(GameCharacter),而不能使用兩個(gè)(GameCharacter, GameLevel),這種情況有些蹩腳。本例中,對(duì)于ebg2的生命值計(jì)算,我們希望始終選用currentLevel作為GameLevel對(duì)象,我們可以將currentLevel作為GemeLevel對(duì)象將兩者“綁定”(bind)起來,這樣,我們?cè)诿看问褂?span style="font-family:"Courier New";">GameLevel::health函數(shù)來計(jì)算ebg2的生命值時(shí),都可以使用綁定好的二者。這就是tr1::bind調(diào)用所做的:他告訴ebg2的生命值計(jì)算函數(shù),應(yīng)該一直指定currentLevel作為GameLevel對(duì)象。

上文中我略去了大量細(xì)節(jié)內(nèi)容,比如說,為什么“_1”意味著“在為ebg2調(diào)用GameLevel::health時(shí)選用currentLevel作為GameLevel對(duì)象”。這些細(xì)節(jié)并不太難理解,而且它們與本節(jié)所講的基本點(diǎn)并無關(guān)系,通過使用tr1::function來代替函數(shù)指針,在計(jì)算角色的生命值時(shí),我們可以讓客戶使用任意可調(diào)用實(shí)體。這是再酷不過的事情了!

“經(jīng)典”策略模式

如果相對(duì)于用C++來???,你更加深諳設(shè)計(jì)模式之道,那么更加傳統(tǒng)的策略模式是這樣實(shí)現(xiàn)的:將生命值計(jì)算函數(shù)聲明為虛函數(shù)。并在此基礎(chǔ)上創(chuàng)建一個(gè)獨(dú)立的生命值計(jì)算層次結(jié)構(gòu)。以下的UML圖體現(xiàn)了這一設(shè)計(jì)方案:


如果UML符號(hào)還是讓你一頭霧水的話,我就簡單點(diǎn)兒說吧:GameCharacter是一個(gè)繼承層次結(jié)構(gòu)中的基類,EvilBadGuyEyeCandyCharacter是派生類;HealthCalcFunc是另一個(gè)繼承層次結(jié)構(gòu)的基類,其下包含SlowHealthLoserFastHealthLoser等派生類。GameCharacter類型的每一個(gè)對(duì)象都有一個(gè)指針,這一指針指向?qū)?yīng)的HealthCalcFunc層次結(jié)構(gòu)中的對(duì)象。

以下是對(duì)應(yīng)的代碼框架:

class GameCharacter;               // 前置聲明

 

class HealthCalcFunc {

public:

  ...

  virtual int calc(const GameCharacter& gc) const

  { ... }

  ...

};

 

HealthCalcFunc defaultHealthCalc;

 

class GameCharacter {

public:

  explicit GameCharacter(HealthCalcFunc *phcf = &defaultHealthCalc)

  : pHealthCalc(phcf)

  {}

 

  int healthValue() const

  { return pHealthCalc->calc(*this);}

  ...

private:

  HealthCalcFunc *pHealthCalc;

};

以上的方案可以使熟悉“標(biāo)準(zhǔn)”策略模式實(shí)現(xiàn)方式的朋友很快的識(shí)別出來,同時(shí),此方案還可以通過為HealthCalcFunc層次結(jié)構(gòu)中添加一個(gè)派生類的方式,使對(duì)現(xiàn)有的生命值算法作出改進(jìn)成為可能。

小節(jié)

本條目中最基本的建議是:在你為當(dāng)前的問題設(shè)計(jì)解決方案時(shí),不妨考慮一下虛函數(shù)以外的其他替代方案。以下是對(duì)上述替代方案的概括:

使用“非虛擬接口”慣用法(NVI慣用法),它是模板方法設(shè)計(jì)模式的一種形式,它將公有非虛成員函數(shù)包裝成為更低訪問權(quán)限的虛函數(shù)。

使用函數(shù)指針數(shù)據(jù)成員代替虛函數(shù)。策略設(shè)計(jì)模式一個(gè)簡裝的表現(xiàn)形式。

使用tr1::function數(shù)據(jù)成員代替虛函數(shù)??梢允褂萌我饪烧{(diào)用實(shí)體,只要實(shí)體特征與所期待的相兼容即可。同樣是策略設(shè)計(jì)模式的一種形式。

使用另一個(gè)層次結(jié)構(gòu)中的虛函數(shù)代替同一層次結(jié)構(gòu)內(nèi)的虛函數(shù)。這是策略設(shè)計(jì)模式的傳統(tǒng)實(shí)現(xiàn)形式。

以上并不是虛函數(shù)替代方案的完整列表,但是這足以說服你虛函數(shù)存在替代方案的。此外,這些方案的比較優(yōu)勢(shì)和劣勢(shì),清楚的告訴你在應(yīng)用的過程中應(yīng)該對(duì)它們加以考慮。

為了避免陷入面向?qū)ο笤O(shè)計(jì)的思維定勢(shì),我們不妨不失時(shí)機(jī)的另辟蹊徑,所謂“條條大路通羅馬”?;ㄐr(shí)間來研究這些替代方案還是頗有裨益的。

時(shí)刻牢記

虛函數(shù)的替代方案包括:NVI慣用法和策略模式的不同實(shí)現(xiàn)方式。NVI慣用法是模板方法設(shè)計(jì)模式的一個(gè)實(shí)例。

將成員函數(shù)的功能挪至類外部存在著以下缺點(diǎn):非成員函數(shù)對(duì)類的非公有成員沒有訪問權(quán)限。

tr1::function對(duì)象就像更一般化的函數(shù)指針。這類對(duì)象支持給定特征的任意可調(diào)用實(shí)體。

posted on 2011-12-25 00:59 ★ROY★ 閱讀(3275) 評(píng)論(2)  編輯 收藏 引用 所屬分類: Effective C++

評(píng)論

# re: 【讀書筆記】[Effective C++中文版第3版][第35條]為虛函數(shù)尋求替代方案   回復(fù)  更多評(píng)論   

不錯(cuò),剛一看還以為是樓主寫的,那不得了的。
好多年沒看過effective c++了,想不到這第3版確實(shí)改進(jìn)不少。
2011-12-25 12:01 | 春秋十二月

# re: 【讀書筆記】[Effective C++中文版第3版][第35條]為虛函數(shù)尋求替代方案   回復(fù)  更多評(píng)論   

用非虛函數(shù)來包裝虛函數(shù)這個(gè)做法雖然早就聽過, 但是我一直沒明白什么場(chǎng)合需要用. 到使用虛函數(shù)包裝非虛函數(shù)的"代理"技術(shù), 倒是時(shí)不常的能用到.
2011-12-25 19:08 | 欲三更
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            在线成人免费观看| 国产精品一区二区在线观看不卡 | 久久久久久9999| 国内精品视频666| 麻豆精品在线视频| 欧美激情精品久久久久久蜜臀 | 日韩一区二区高清| 一区二区三区蜜桃网| 国产精品一区二区在线观看网站| 久久久久国产一区二区三区| 久久日韩精品| 亚洲无限av看| 久久精品99久久香蕉国产色戒| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲高清一二三区| 日韩午夜精品视频| 国模 一区 二区 三区| 亚洲高清毛片| 国产精品美女久久| 欧美大色视频| 国产精品九色蝌蚪自拍| 欧美电影免费| 国产精一区二区三区| 亚洲第一区在线观看| 国产精品外国| 亚洲国产乱码最新视频| 国产视频欧美视频| 亚洲美女在线观看| 亚洲高清资源| 欧美一区二区三区免费看| 中日韩美女免费视频网址在线观看| 久久国产精品一区二区三区四区| 一本色道久久综合亚洲精品不 | 黑人极品videos精品欧美裸| 9l视频自拍蝌蚪9l视频成人 | 欧美激情在线狂野欧美精品| 久久久久久久国产| 国产精品日韩欧美大师| 亚洲欧洲精品一区| 亚洲高清av| 久久精品国产69国产精品亚洲 | 亚洲无线一线二线三线区别av| 久久先锋影音| 久久婷婷蜜乳一本欲蜜臀| 国产精品久久久久久亚洲调教| 亚洲韩日在线| 91久久精品美女高潮| 欧美专区在线播放| 欧美在线视频观看免费网站| 欧美日韩一区二区在线播放| 亚洲国产高清aⅴ视频| 在线看无码的免费网站| 久久精品一本久久99精品| 久久国产精品久久精品国产| 欧美性色综合| 中文av字幕一区| 亚洲午夜一级| 国产精品成人观看视频免费 | 亚洲高清视频一区| 亚洲日本久久| 欧美片在线播放| 亚洲精美视频| 正在播放亚洲| 国产精品成人在线观看| 亚洲夜间福利| 久久精品在这里| 一区二区视频欧美| 免费观看亚洲视频大全| 亚洲电影在线| 国产精品99久久久久久久vr| 欧美三级电影一区| 亚洲视频一区二区在线观看| 午夜视频一区二区| 国产一区二区主播在线| 久久久精彩视频| 亚洲国产日韩欧美在线图片| 日韩一本二本av| 国产精品国产自产拍高清av| 午夜一区二区三视频在线观看| 久久精品1区| 亚洲激情在线| 欧美小视频在线| 欧美一区二区三区婷婷月色| 欧美成人午夜激情在线| 一区二区久久久久久| 国产精品专区h在线观看| 久久精品99国产精品日本| 亚洲电影在线观看| 欧美亚洲一区二区三区| 在线观看欧美日韩| 国产精品www网站| 久久久久久久一区二区三区| 亚洲免费观看高清完整版在线观看熊| 亚洲素人在线| 在线免费高清一区二区三区| 欧美日本国产一区| 欧美一级在线视频| 亚洲区在线播放| 久久精品国产清高在天天线| 亚洲精品精选| 国产午夜精品在线| 欧美日韩国产影片| 久久久精品国产免大香伊| 99精品视频一区| 欧美暴力喷水在线| 香蕉久久国产| av成人免费观看| 精品动漫3d一区二区三区免费| 欧美日韩精品一区二区三区四区 | 亚洲三级网站| 欧美jizz19性欧美| 久久av在线| 亚洲一区免费网站| 亚洲毛片在线| 亚洲第一区在线| 国产日韩欧美亚洲一区| 欧美香蕉大胸在线视频观看| 欧美成人免费大片| 久久精品在线免费观看| 亚洲欧美国产va在线影院| 亚洲精品国精品久久99热| 欧美a级一区二区| 久久久久久久一区二区| 欧美在线播放视频| 亚洲综合精品自拍| 一区二区三区欧美| 一本色道88久久加勒比精品| 亚洲国产精品精华液网站| 国内精品久久久久久久影视麻豆| 国产精品三上| 国产精品亚洲综合一区在线观看| 欧美日韩综合网| 欧美裸体一区二区三区| 欧美精品导航| 欧美精品亚洲一区二区在线播放| 久久免费视频网站| 久久一区二区三区国产精品| 久久女同互慰一区二区三区| 久久精品99国产精品| 久久久久久久性| 久久一区视频| 欧美韩国日本综合| 欧美区日韩区| 欧美午夜精品| 国产精品一区二区在线观看不卡 | 国产日韩在线一区| 合欧美一区二区三区| 禁久久精品乱码| 在线看无码的免费网站| 亚洲三级毛片| 亚洲视频二区| 欧美在线日韩在线| 久久综合婷婷| 欧美激情在线| 日韩一区二区精品视频| 亚洲婷婷综合色高清在线| 午夜日韩在线观看| 久久精品30| 欧美精品久久久久久久久老牛影院| 欧美日韩黄色大片| 国产精品一区二区久久| 影院欧美亚洲| 在线亚洲精品| 久久精品天堂| 亚洲第一精品福利| 一二美女精品欧洲| 久久久久久69| 欧美视频观看一区| 狠狠入ady亚洲精品| 一二美女精品欧洲| 久久久久久久综合| 亚洲精品美女91| 欧美在线视频一区二区| 欧美日本中文| 激情欧美一区二区| 宅男精品视频| 免费不卡视频| 亚洲中字黄色| 欧美—级a级欧美特级ar全黄| 国产精品日韩一区| 日韩一级网站| 免费成人你懂的| 亚洲一区三区电影在线观看| 麻豆成人在线播放| 国产农村妇女精品| 一区二区三区日韩欧美| 久久婷婷人人澡人人喊人人爽| 日韩午夜高潮| 欧美成人精品在线播放| 国产亚洲综合精品| 亚洲欧美电影院| 亚洲精品久久久久久下一站| 久久精品综合一区| 国产精品一二| 亚洲天堂成人| 亚洲三级性片| 欧美国产日韩在线| 在线精品福利| 久久亚洲综合色| 午夜精品久久久久影视|