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

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

C++ 泛型編程系列講座之實施

2019-11-17 05:40:15
字體:
來源:轉載
供稿:網友

  你知道,當一個概念從一個專有名詞變成一個普通名詞時,說明它真正的深入人心了。比如Kleenex(面巾紙品牌,也指面巾紙),Xerox(施樂,復印機品牌,也指復印機)Q-Tips(化妝品品牌,也指化妝包),對嗎?所以說,當我聽說你可以在Visual C++.NET中使用“modern C++ design”時非常興奮也就不希奇了。這里指的是——至少我這樣認為——《Modern C++ Design》[1]所推行的基于模板的技術。

  在所有泛型編程文章中都有一個成功的要素,就是進行特有至通用的“文法升華?!北热?,在ScopeGuard[2]中增加了放入“撤消”動作于正常執行路徑而當一個復雜操作成功時解除“撤消”的常見技術之后,ScopeGuard就成為了“域守衛(scope guard)”。我大多數受歡迎的文章不完全由我完成,而是和Petru Marginean緊密合作的結果。而且讓我更加興奮的是再一次在本文中與他合作。

  本文中我們將討論對應于發布狀態的機制:實施(enforcement),這是方便實用的快速條件驗證機制。非常類似于ScopeGuard,ENFORCE宏極大程度地減少了你需要使用在錯誤處理上的代碼。ScopeGuard和ENFORCE可相互獨立工作,但最好是一起使用。ENFORCE是意外的源頭,ScopeGuard是意外的傳播者。

  實施(Enforcements)

  設想你注視著面前的一大堆代碼。你知道你要在這些代碼中殺出一條血路,你也知道你必須寫更多的新代碼。

  一個好習慣是先瀏覽代碼,試著找到一些通用模式。你要理解模式背后的概念。很有可能,所有這些都不是偶然發生的,而是同樣的基本概念的不同表現形式,然后,你可以整理這些提取出的模式,這樣你就可以扼要地表達所有的作為概念的實現體的模式

  現在先暫停,看一下你要分析的不同模式的大小。假如這個模式很大,比如作為基礎部分涉及到了整個程序,那么你正在和結構模式(architectural patterns)打交道。

  假如模式是中等尺寸,橫跨數個對象和/或函數,那么你碰到的是設計模式。

  假如模式非常小,只包括3-10行代碼,那么你面前的的常用法(idiom)。

  最后,假如模式只有1-2行代碼,你得到的東西叫做代碼風格或格式。

  這四種根據規模劃分的類型所涵蓋的范圍類似于建筑學。在建筑學中,基本結構的小瑕疵不要緊。在軟件架構中,任何缺陷都可能毀掉整個“建筑”。相反,你需要在任何范圍使用正確的技術來確保成功。假如你對小細節著迷而忽略大的藍圖,你會浪費才華于建立不能遠觀的復雜的阿拉伯式建筑。假如你只注重大的方面而忽視細節,你會得到粗糙的龐然大物。

  這是寫軟件異常困難的原因,這個困難是在其他領域工作的人們不能完全理解的。

  實施屬于常用法范疇。具體點說,實施極大簡化了錯誤檢測代碼而不會影響可讀性和正常工作流的流暢性。

  想法來源于下列事實。你拋出一個意外非常可能是作為一個布爾值檢測的結果,如下:

if (some test)
 throw SomeException(arguments);
  假如該意外在多處出現,為什么不把它放在一個小函數里呢:

template <class E, class A>
inline void Enforce(bool condition A arg)
{
 if (!condition) throw E(arg);
}
  可以這樣使用它:

Widget* p = MakeWidget();
Enforce<std::runtime_error>(p != 0, “null pointer”);
Enforce<std::runtime_error>(cout != 0, “cout is in error”);
cout << p->ToString();
  目前為止一切順利.現在,我們來分析幾個重要方面。

  首先,被測條件不總是布爾型的,也可能是一個指針或整型。其次,非常可能你要在檢測完之后馬上使用被測值。比如,你可能希望在使用前確保一個指針非空,或你可能要在創建一個文件句柄后馬上使用它。所以我們修改Enforce,這樣它就有濾過機制來把接受的值傳回:

template <class E, class A, class T>
inline T& Enforce(T& obj, A arg)
{
 if (!obj) throw E(arg);
  return obj;
}

template <class E, class A, class T>
inline const T& Enforce(const T& obj, A arg)
{
 if (!obj) throw E(arg);
  return obj;
}
  (兩個版本是必須的,分別對應const和非const對象。)你可以增加兩個重載版本來表示你通常會拋出的那種意外和它所帶參數。

Template <class T>
Inline T& Enforce(T& obj, const char* arg)
{
 return this->Enforce<std::runtime_error, const char*, T>(obj,arg);
}

template <class T>
inline const T& Enforce(const T& obj, const char* arg)
{
 return this->Enforce<std::runtime_error, const char*, T>(obj,arg);
}
  假如你認為應該傳入一個通用參數(信息)到std::runtime_error,調用可以進一步簡化。你所需做的一些只是增加幾個重載函數:


Template <class T>
Inline T& Enforce(T& obj)
{
 return this->EnforceMstd::runtime_error, const char*, T>(obj,“Enforcement error”);
}

template <class T>
inline const T& Enforce(const T& obj)
{
 return this->Enforce<std::runtime_error, const char*, T>(obj,“Enforcement error”);
}
  現在,隨著這些簡單的擴展,代碼變得相當得有表現力:

Enforce(cout) << Enforce(MakeWidget())->ToString();
  在一行中你不單創建了一個widget對象并把它打印到控制臺,你還標明在這個過程中可能會發生的任何錯誤!你也許還要自動釋放創建的Widget對象,只需要在里面添加auto_ptr:

Enforce(cout) <<
Enforce(auto_ptr(MakeWidget()))->ToString();
  哇!非常好——非凡是拿它和其他解決方案相比較時。

  在不中斷正常執行流程的情況下,Enforce漂亮地過濾掉錯誤。這樣,Enforce提供了一個方便的手段來檢查和清除錯誤情況。

  盡量讓程序員方便地處理錯誤是非常重要的。這是因為錯誤處理經常,不幸的,被認為是白費力氣的工作。經理們不會把錯誤處理作為考核標準。結果是,匆忙的,過度操勞的,預備不足的[3]程序員們在那咬著手指頭期盼cout會總是處于一個良好的狀態,MakeShape也決不返回空指針。但咬手指頭并不是一個真正好的編程方法。 修飾Enforce

  上面代碼中所顯示的“Enforcement failed”信息并不是很有用,所以我們需要對它做點修改。幸運的是,Petru的靈感一發而不可收拾。“那個腦袋從不停止工作!”

  首先,包含在錯誤通知中的好的信息應該有不討人喜歡的__FILE__和__LINE__。同時,能看到失敗的表達式也會很有幫助。就象我們在Asserter[4]中所做的,我們建立一個小小的類來為我們保存這些信息:

template <class Ref>
class Enforcer
{
 Ref obj_;
 const char* const locus_;
 public:
  Enforcer(Ref obj, const char* locus) : obj_(obj), locus_(locus) {}
  Ref Enforce()
  {
   if (!obj_) throw std::runtime_error(locus_);
    return obj_;
  }
};
  obj_成員保存被檢測的對象。Locus_成員是上述關于文件,行數,和表達式的信息。

  為什么我們把Enforcer的模板參數叫做Ref而不是傳統的T?原因是我們要總是用一個引用類型(不是一個值類型)來實例化Enforce,這會在接下去減少我們很多重復勞動。(假如你曾經寫過類似的對const和非const引用的函數,你就會知道我的意思)

  好吧,現在創建Enforcer對象,我們用一個小函數,這樣我們可以輕松一點,讓它來做類型推斷:

template <typename T>
inline Enforcer<const T&>
MakeEnforcer(const T& obj, const char* locus)
{
 return Enforcer<const T&>(obj, locus);
}

template <typename T>
inline Enforcer<T&>
MakeEnforcer(const T& obj, const char* locus)
{
 return Enforcer<T&>(obj, locus);
}

  我們現在只需要給蛋糕裱上奶油——意料之中的宏。

  我們知道你討厭宏,而且討厭宏的不在少數,但我們更討厭重復打__FILE__和__LINE__:

#define STRINGIZE(something) STRINGIZE_HELPER(something)
#define STRINGIZE_HELPER(something) #something
#define ENFORCE(eXP) /
MakeEnforcer((exp), "Ex__FILE__ "', line: "STRINGIZE(__LINE__)).Enforce()
  STRINGIZE和STRINGIZE_HELPER宏是預編譯器必須的復雜過程來把__LINE__轉換為數字。(不,#__LINE__沒有用。)我從來都不完全知道這些宏怎樣和為什么起作用(這和預編譯器部分有關…啊,我腦海里開始涌現那悲慘的回憶!停下來,醫生?。遥拱渍f,我情愿去了解紐約城的下水道系統怎樣運作也不想知道這里的細節。只要說STRINGIZE(__LINE__)產生了一個包含現在行數的字符串就足夠了。那些這方面的專家[6]提供了完整的解釋。

  本專欄的一貫傳統是不涉及編譯器特性,所以我們只順便提一下STRINGIZE技巧在MSVC的預編譯器上會產生類似于(__LINE__VAR+7)的神秘字符串。

  令人興奮的一面是,Enforcer的初始化代價只有兩個指針賦值那樣低廉,同時卻保存了非常有用的信息。你可以方便地增加關于文件日期和編譯時間的信息,以及非標準的信息,比如__FUNCTION__。

  支持多個參數及ENFORCE的自定義判定條件

  ENFORCE是個很好的想法,但假如你用了某樣東西卻發現在實際應用中卻不如文章中宣稱的那么有用,你不會感到受欺騙了嗎?

  我們會,并且我們已經發現ENFORCE中兩個重要缺陷。

  首先,通常更需要在默認的文件名,行數,和表達式信息的基礎上增加——或代之以——傳入一個自定義字符串的功能。

  其次,ENFORCE只用!操作符來檢測非零條件。然而,在真實應用中,有時候需要被檢查的“錯誤”值不是零。許多使用整數返回值的API,包括在<io.h>中的標準C文件函數,返回-1來標識一個錯誤。另一些API使用一個符號常量。而COM使用更復雜的情況:假如返回值為零(就是S_OK),表示正常,假如返回值小于零,說明有一個錯誤,并且返回的實際值給出了錯誤的信息。假如返回值大于零,狀態就是“帶信息的成功”,就是說返回值中有一些有用的信息[5]。

  顯然我們需要一個更靈活的檢測和報告框架。我們需要能夠在兩個層面上配置實施(判定條件和參數傳入機制),最好在編譯時配置,這樣實施機制比同等的手寫代碼不會有更多的開銷。(有一個明智的檢查總是需要做一下:當某種抽象應用于具體情況,是否能比得上非抽象的解決方法?)

  基于策略的(Policy-based)設計正適合于解決此問題。所以Enforce需要從一個簡單的類改進為一個雙策略參數的模板類。第一個策略是判定條件策略(處理檢測事宜),第二個策略是拋出策略(處理構建和拋出意外對象)。


template<typename Ref, typename P, tyoename R>
class Enforcer
{
 …使用兩個策略(看下一部分)…
}
  兩個策略都有非常簡單的接口。以下是默認策略:

strUCt DefaultPredicate
{
 template <class T>
 static bool Wrong(const T& obj)
 {
  return !obj;
 }
}
struct DefaultRaiser
{
 template <class T>
 static void Throw(const T&, const std::string& message, const char* locus)
 {
  throw std::runtime_error (message + ‘/n’ + locus);
 }
} 實現細節(和漂亮的技巧)

  好的,現在讓Enforcer使用它的兩個策略來檢測值并拋出意外應該很簡單。

  當出現錯誤時假如能讓用戶來指定任意信息格式將會非常有用;進一步,除非一個意外確實被拋出,這些信息(可能會影響運行速度)應該被避免。一些靈感加99%的汗水,我們設計了一個能夠滿足這些要求的機制。

  我們先展示代碼然后做說明。最終的Enforcer類如下所示:

template <typename Ref, typename P, typename R>
class Enforcer
{
 public:
  Enforcer(Ref t, const char* locus) : t_(t), locus_(P::Wrong(t) ? locus : 0)
  {
  }
  Ref
Operator*() const
  {
   if (locus_) R::Throw(t_, msg, locus_);
    return t_;
  }
  template <class MsgType>
  Enforcer& operator()(const MsgType& msg)
  {
   if (locus_)
   {
    //執行到這里我們就有的是時間,不必有太高效率
    std::ostringstream ss;
    ss <<msg;
    msg_ += ss.str ();
   }
   return *this;
  }
  private:
   Ref t_;
   std::string msg_;
   const char* const locus_;
 };
 template <class P, class R, typename T>
 inline Enforcer<const T&, P, R>
 MakeEnforcer(const T& t, const char* locus)
 {
  return Enforcer<const T&, P, R>(t, locus);
 }
 template <class P, class R, typename T>
 inline Enforcer<T&, P, R>
 MakeEnforcer(T& t, const char* locus)
 {
  return Enforcer<T&, P, R>(t, locus);
 }
 #define ENFORCER(exp) /
 *MakeEnforcer<DefaultPredicate, DefaultRaiser>(/(exp), “Expression ‘” #exp “’ failed in ‘ ” /
__FILE__ ‘”, line: “ STRINGIZE(__LINE__))

  非常好,這樣Enforce定義了兩個操作符函數:operator*和模板化的operator()。而且注重ENFORCE宏將“*”放在MakeEnforcer調用之前。所有這些的工作原理是什么?為什么要這些輔助代碼?

  假設你寫下如下代碼:

Widget* pWidget = MakeWidget();
ENFORCE(pWidget);
  ENFORCE宏擴展為:

*MakeEnforcer<DefaultPredicate, DefaultRaiser>(fpWidget),“Expression ‘pWidget’ failed in ‘blah.cpp’, line: 7”)
  MakeEnforcer被調用后創建下面類型的一個對象:

Enforcer<const Widget*&, DefaultPredicate, DefaultRaiser>
  該對象的創建使用了兩個參數的構造函數。請注重,locus_只有當P::Wrong(t)為真時才被初始化為一個非空指針。換句話說,locus_只有在意外應該被拋出時才指向有用信息,否則為空。

  對于被創建的對象調用operator*函數。不出意料的,假如locus_非空,R::Throw被調用,否則被檢測的對象只是被傳回。

  繼續看一個更有趣的例子,代碼如下:

Widget* pWidget = MakeWidget ();
ENFORCE(pWidget)(“This widget is null and it shouldn’t!”);
  此處當Enforcer對象被創建后,operator()被調用。該操作要么把傳入信息添加到msg_成員變量中,要么假如pWidget非空即無錯誤出現就忽略所有。換句話說,正常執行路徑與帶一個檢測的執行路徑執行得一樣快。這就是漂亮的地方——真正的工作只在發生錯誤的情況才做,

  因為operator()是模板化的并且使用一個std::ostringstream,它支持一切你可以傳到cout去的東西。而且,operator()返回*this,這樣你可以把對它的連續調用連接起來,下面例子展示了這一點:


int n = …;
Widget* pWidget = MakeWidget(n);
ENFORCE(pWidget)(“Widget number “)(n)(“ is null and it shouldn’t!”);
  我們不知道你感覺如何,反正我們對這個設計十分的滿足。反過來說,誰會不喜歡一個簡明的,富于表達力的,并且是高效的解決方案呢? 定制判定和拋出策略

  基于策略的Enforcer提供了重要的鉤子來答應無限的變化。比如,檢測句柄值不為-1(而不是檢測不為零)只需要六行代碼:

struct HandlePredicate
{
 static bool Wrong(long handle)
 {
  return handle == -1;
 }
};

#define HANDLE_ENFORCE(exp)/
*MakeEnforcer<HandlePredicate, DefaultRaiser>((exp), “Expression ‘” #exp “’ failed in ‘” / __FILE__”’, line : “ STRINGIZE(__LINE__))
  別忘了Enforce返回傳入的值,這給了客戶代碼帶來極大的表現力:

const int available = HANDLE_ENFORCE(_read(file, buffer, buflen));
  上面從一個文件讀取數據,記錄讀取的字節數,并且標志可能出現的錯誤。一行搞定,酷吧!

  類似的,你能夠為你的應用程序方便弟定義適合的新策略和XYZ_ENFORCE宏。對應每個錯誤編碼規范可以有一個XYZ_ENFORCE宏。多數應用程序使用1到4種不同規范。實際操作中我們碰到過下列常用規范:

  1、我們上面討論過的基本ENFORCE。用operator!檢查并且拋出std::runtime_exception。

  2、HANDLE_ENFORCE。檢測不為-1并且拋出某種意外。

  3、COM_ENFORCE。當結果為負就作為錯誤處理。拋出策略從COM返回代碼中提取錯誤信息并且放入要被拋出的意外對象中。我們相信這個工具在寫重要的COM應用程序時是真正的無價之寶

  4、CLIB_ENFORCE。許多在C標準庫中的函數錯誤時返回零并要你來檢查errno獲知錯誤詳情。假如有一個能夠把errno轉為文本并將文本放入要拋出的意外中的漂亮的Raiser策略,CLIB_ENFORCE就會非常完美。

  5、WAPI_ENFORCE。某些Windows API函數成功時返回零失敗時返回負的錯誤代碼。

  適應新的錯誤編碼規范是一件非常簡單的事情。

  結論

  我們發現值的自動實施檢查極其有用,甚至于擁有它等于擁有樂趣,失去它寸步難行。實施不是把幾行代碼濃縮為一行代碼那么簡單。實施讓你集中精力于應用的正常執行流,自然而靈活地過濾掉不需要的值。這種過濾是通過返回其傳入參數的過濾函數實現的。

  用了幾個宏技巧,付出了非常低的運行時開銷,增加了有用信息

  通過模板參數的參數化實現了基于策略的設計,它不僅強大在理論上,而且實踐中同樣如此?;诓呗缘姆椒óa生的設計有低運行時開銷(和手寫的if同樣?。┖湍軌蜻m應大多數非凡錯誤編碼規范的高可配置框架。

  我們已用Microsoft Visual C++.NET Everett Beta和gcc 3.2測試過附加代碼。放心用吧!

  參考和注釋

  [1] A. Alexandrescu. Modern C++ Design (Addison-Wesley Longman, 2001).
  [2] Andrei Alexandrescu and Petru Marginean. "Simplify your Exception-Safe Code"
  [3] 預備不足意思是讓一名程序員接手先天不足的具體設計。
  [4] Andrei Alexandrescu. "Assertions"
  [5] 這個規范看上去很好。但不知什么原因演變成舊規范。在COM世界中,S_TRUE是零,S_FALSE是1。不要忽略這個信息!
  [6] http://www.jaggersoft.com/pubs/CVu10_1.Html

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品视频免费播放| 亚洲中国色老太| 夜夜躁日日躁狠狠久久88av| 国产精品中文字幕久久久| 久久久久这里只有精品| 成人福利视频在线观看| 5278欧美一区二区三区| 九九九久久国产免费| 一个人看的www久久| 欧美一性一乱一交一视频| 4438全国成人免费| 亚洲精品成a人在线观看| 亚洲 日韩 国产第一| 国产欧美一区二区三区久久| 精品久久久久久久大神国产| 亚洲视频在线免费看| 久精品免费视频| 成人在线中文字幕| 国产成人一区二区三区电影| 亚洲国产精品免费| 91九色国产视频| 91在线观看免费高清完整版在线观看| 亚洲丝袜在线视频| 精品亚洲一区二区三区在线观看| 欧美成人性色生活仑片| 久久久中文字幕| 日韩av在线免播放器| 成人在线中文字幕| 国产精品欧美亚洲777777| 久久6精品影院| 欧美成人精品一区二区三区| 欧美亚洲一级片| 国产精品夜间视频香蕉| 欧美激情视频免费观看| 4k岛国日韩精品**专区| 久久久www成人免费精品张筱雨| 亚洲大尺度美女在线| 97精品国产91久久久久久| 91久久精品久久国产性色也91| 精品视频在线播放免| 日本精品久久中文字幕佐佐木| 国内成人精品视频| 国产午夜精品久久久| 国产精品久久久久久av福利| 精品国产成人av| 精品福利免费观看| 亚洲欧美中文另类| 欧美日韩国产在线| 亚洲人精品午夜在线观看| 欧美精品电影免费在线观看| 91av在线精品| 日韩女优人人人人射在线视频| 欧美精品videofree1080p| 秋霞午夜一区二区| 97婷婷涩涩精品一区| 久久福利网址导航| 国产精品久久久久久久久借妻| 在线观看日韩www视频免费| 日韩一区二区精品视频| 欧美成人午夜激情在线| 欧美日韩在线第一页| 欧美贵妇videos办公室| 日韩亚洲国产中文字幕| 成人av番号网| 高跟丝袜一区二区三区| 97热精品视频官网| www.欧美视频| 国产日韩欧美在线观看| 精品一区电影国产| 亚洲加勒比久久88色综合| 欧洲亚洲免费在线| 91国偷自产一区二区三区的观看方式| www.亚洲成人| 日韩一区二区av| 亚洲国产精品高清久久久| 秋霞av国产精品一区| 日韩在线观看视频免费| 久久久久久中文字幕| 在线精品国产成人综合| 国产视频久久网| 欧美综合国产精品久久丁香| 色综合久久中文字幕综合网小说| 久久久精品久久久| 亚洲国产精久久久久久久| 日韩av电影手机在线观看| 亚洲色在线视频| 欧美日本在线视频中文字字幕| 国产一区二区三区四区福利| 中文字幕自拍vr一区二区三区| 亚洲开心激情网| 欧美xxxx做受欧美.88| 亚洲a成v人在线观看| 91免费视频国产| 2019av中文字幕| 97视频在线观看播放| 欧美福利在线观看| 国产原创欧美精品| 永久免费看mv网站入口亚洲| 国产精品毛片a∨一区二区三区|国| 亚洲视频在线观看网站| 国产欧美精品一区二区三区-老狼| 97成人超碰免| 久久精品青青大伊人av| 国产激情视频一区| 亚洲性日韩精品一区二区| 亚洲精品综合久久中文字幕| 日韩电影大片中文字幕| 日韩av在线看| 亚洲精品一区二三区不卡| 日韩av在线免费观看| 欧美午夜激情小视频| 成人a在线视频| 日本久久91av| 欧美激情一区二区久久久| 亚洲女性裸体视频| 久久国产精品亚洲| 欧美精品情趣视频| 亚洲午夜久久久久久久| 亚洲欧洲自拍偷拍| 亚洲男人天堂网站| 日韩av影视在线| 欧美一级大片在线免费观看| 中文字幕久久久av一区| 国产精品一区二区性色av| 久国内精品在线| 久久精品一区中文字幕| 亚洲色图综合网| 欧美夫妻性生活视频| yw.139尤物在线精品视频| 欧美日韩精品二区| 亚洲精品国产品国语在线| 在线国产精品播放| 色悠悠久久久久| 国产91精品在线播放| 欧美丰满片xxx777| 亚洲丁香婷深爱综合| 中文字幕久热精品视频在线| 久久男人资源视频| 国产精品久久久久77777| 欧美大尺度电影在线观看| 国产日韩在线一区| 久久久久久91| 国产精品欧美激情| 欧美极品在线视频| 2024亚洲男人天堂| 2023亚洲男人天堂| 久久国产精彩视频| 欧美精品videofree1080p| 国产精品久久久久久久久久久不卡| 精品久久久久久久久国产字幕| 欧美大成色www永久网站婷| 亚洲第一精品久久忘忧草社区| 亚洲精品国产suv| 在线电影欧美日韩一区二区私密| 日韩在线中文字幕| 亚洲精品国产综合区久久久久久久| 日本精品久久久久影院| 欧美另类老肥妇| 久久九九亚洲综合| 亚洲视频在线视频| 日韩成人在线免费观看| 91视频免费在线| 成人午夜小视频| 91精品国产色综合久久不卡98口|