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

洛譯小筑

別來無恙,我的老友…
隨筆 - 45, 文章 - 0, 評論 - 172, 引用 - 0
數據加載中……

[ECPP讀書筆記 條目3] 盡可能使用const

const令人贊嘆之處就是:你可以通過它來指定一個語義上的約束——一個特定的不能夠更改的對象——這一約束由編譯器來保證。通過一個const,你可以告訴編譯器和其他程序員,你的程序中有一個數值需要保持恒定不變。不管何時,當你需要這樣一個數時,你都應確保對這一點做出聲明,因為這樣你便可以讓編譯器來協助你確保這一約束不被破壞。

const關鍵字的用途十分廣泛。在類的外部,你可以利用它定義全局的或者名字空間域的常量(參見條目2),也可以通過添加static關鍵字來定義文件、函數、或者程序塊域的對象。在類的內部,你可以使用它來定義靜態的或者非靜態的數據成員。對于指針,你可以指定一個指針是否是const的,其所指的數據是否是const的,或者兩者都是const,或者兩者都不是。

char greeting[] = "Hello";

char *p = greeting;                // const指針,非const數據

const char *p = greeting;         // const指針,const數據

char * const p = greeting;        // const指針,非const數據

const char * const p = greeting;  // const指針,const數據

這樣的語法乍一看反復無常,實際上并非如此。如果const關鍵字出現在星號的左邊,那么指針所指向的就是一個常量;如果const出現在星號的右邊,那么指針本身就是一個常量;如果const同時出現在星號的兩邊,那么兩者就都是常量。

當指針所指的內容為常量時,一些程序員喜歡把const放在類型之前,其他一些人則喜歡放在類型后邊,但要在星號的前邊。這兩種做法沒有什么本質的區別,所以下邊給出的兩個函數聲明的參數類型實際上是相同的:

void f1(const Widget *pw);        // f1傳入一個指向 Widget對象常量的指針

void f2(Widget const *pw);        // f2也一樣

由于這兩種形式在實際代碼中都會遇到,所以二者你都要適應。

STL迭代器是依照指針模型創建的,所以說iterator更像一個T*指針。把一個iterator聲明為 const的更像是聲明一個const指針(也就是聲明一個T* const指針):iterator不允許指向不同類型的內容,但是其所指向的內容可以被修改。如果你希望一個迭代器指向某些不能被修改的內容(也就是指向const T*的指針),此時你需要一個const_iterator

std::vector<int> vec;

...

const std::vector<int>::iterator iter = vec.begin();

                                   // iter就像一個T* const

*iter = 10;                        // 正確,可以改變iter所指向的內容

++iter;                            // 出錯!Iter是一個const

 

std::vector<int>::const_iterator cIter = vec.begin();

                                   // cIter就像一個const T*

*cIter = 10;                       // 出錯!*cIter是一個const

++cIter;                           // 正確,可以改變cIter

const在函數聲明方面還有一些強大的用途。在一個函數聲明的內部,const可以應用在返回值、單個參數,對于成員函數,可以將其本身聲明為const的。

讓函數返回一個常量通常可以在兼顧安全和效率問題的同時,減少客戶產生錯誤的可能。好比有理數乘法函數(operator*)的聲明,更多信息請參見條目24。

class Rational { ... };

const Rational operator*(const Rational& lhs, const Rational& rhs);

很多程序員在初次到這樣的代碼時都不會正眼看一下。為什么operator*要返回一個const對象呢?這是因為如果不是這樣,客戶端將會遇到一些不愉快的狀況,比如:

Rational a, b, c;

...

(a * b) = c;                       // 調用operator= 能為a*b的結果賦值!

我不知道為什么一些程序員會企圖為兩個數的乘積賦值,但是我確實知道好多程序員的初衷并非如此。他們也許僅僅在錄入的時候出了個小差錯(他們的本意也許是一個布爾型的表達式):

if (a * b = c) ...                 // 啊哦本來是想進行一次比較!

如果ab是內建數據類型,那么這樣的代碼很明顯就是非法的。避免與內建數據類型不必要的沖突,這是一個優秀的用戶自定義類型的設計標準之一(另請參見條目18),而允許為兩數乘積賦值這讓人看上去就很不必要。如果將operator*的返回值聲明為const的則可以避免這一沖突,這便是要這樣做的原因所在。

const參數沒有什么特別新鮮的——它與局部const對象的行為基本一致,你在應該在必要的時候盡可能使用它們。除非你需要更改某個參數或者局部對象,其余的所有情況都應聲明為const。這僅僅需要你多打六個字母,但是它可以使你從惱人的錯誤(比如我們剛才見到的“我本想打’==’但是卻打了’=’”)中解放出來。

const成員函數

對成員函數使用const的目的是:指明哪些成員函數可以被const對象調用。這一類成員函數在兩層意義上是十分重要的。首先,它們使得類的接口更加易于理解。很有必要了解哪些函數可以對對象做出修改而哪些不可以。其次,它們的出現使得與const對象協同工作成為可能。這對于高效編碼來說是十分關鍵的一個因素,這是由于(將在條目20中展開解釋)提高C++程序性能的一條最基本的途徑就是:傳遞對象的const引用。使用這一技術需要一個前提:就是必須要有const成員函數存在,只有它們能夠處理隨之生成的const對象。

如果若干成員函數之間的區別僅僅為“是否是const的”,那么它們也可以被重載。很多人都忽略了這一點,但是這是C++的一個重要特征。請觀察下面的代碼,這是一個文字塊的類:

class TextBlock {

public:

  ...

  const char& operator[](std::size_t position) const

  { return text[position]; }       // operator[] :用于const對象

 

  char& operator[](std::size_t position)

  { return text[position]; }       // operator[] :用于非const對象

 

private:

   std::string text;

};

TextBlockoperator[]可以這樣使用:

TextBlock tb("Hello");

std::cout << tb[0];                // 調用非constTextBlock::operator[]

 

const TextBlock ctb("World");

std::cout << ctb[0];               // 調用 constTextBlock::operator[]

順便說一下,在真實的程序中,const對象在大多數情況下都以“傳遞指針”或“傳遞const引用”的形式出現。 上面的ctb的例子純粹是人為的,而下面的例子在真實狀況中常會出現:

void print(const TextBlock& ctb)  // 在這個函數中ctbconst

{

  std::cout << ctb[0];             // 調用constTextBlock::operator[]

  ...

}

通過對operator[]的重載以及為每個版本提供不同類型的返回值,你便可以以不同的方式處理const的或者非constTextBlock

std::cout << tb[0];                // 正確:讀入一個非constTextBlock

tb[0] = 'x';                       // 正確:改寫一個非constTextBlock

std::cout << ctb[0];               // 正確:讀入一個constTextBlock

ctb[0] = 'x';                      // 錯誤! 不能改寫constTextBlock

請注意,這一錯誤只與所調用的operator[]的返回值的類型有關,然而對operator[]調用本身的過程則不會出現任何問題。錯誤出現在企圖為一個const char&賦值時,這是因為const char&operator[]const版本的返回值類型。

同時還要注意的是,非constoperator[]的返回值是一個char的引用,而不是char本身。如果operator[]真的簡單的返回一個char,那么下面的語句將不能正確編譯:

tb[0] = 'x';

這是因為,企圖修改一個返回內建數據類型的函數的返回值根本都是非法的。即使假設這樣做合法,而C++是通過傳值返回對象的,所修改的僅僅是由tb.text[0]復制出的一份副本,而不是tb.text[0]本身,你也不會得到預期的效果。

讓我們暫停一小會兒,來考慮一下這里邊的哲學問題。把一個成員函數聲明為const的有什么涵義呢?這里有兩個流行的說法:按位恒定(也可叫做物理恒定)和邏輯恒定。

按位恒定陣營堅信:當且僅當一個成員函數對于其所在對象所有的數據成員(static數據成員除外)都不做出改動時,才需要將這一成員函數聲明為const,換句話說,將成員函數聲明為const的條件是:成員函數不對其所在對象內部做任何的改動。按位恒定有這樣一個好處,它使得對違反規則行為的檢查十分輕松:編譯器僅需要查找對數據成員的賦值操作。實際上,按位恒定就是C++對于恒定的定義,如果一個對象調用了某個const成員函數,那么該成員函數對這個對象內所有非靜態數據成員的修改都是不允許的。

不幸的是,大多數不完全const的成員函數也可以通過按位恒定的測試。在特定的情況下,如果一個成員函數頻繁的修改一個指針所指的位置,那么它就不應是一個const成員函數。但是只要這個指針存在于對象內部,這個函數就是按位恒定的,這時候編譯器不會報錯。這樣會導致違背常理的行為。比如說,我們手頭有一個類似于TextBlock的類,其中保存著char*類型的數據而不是string,因為這段代碼有可能要與一些C語言的API交互,但是C語言中沒有string對象一說。

class CTextBlock {

public:

  ...

  char& operator[](std::size_t position) const

  // operator[]不恰當的(但是符合按位恒定規則)定義方法

  { return pText[position]; }

 

private:

  char *pText;

};

盡管operator[]返回一個對象內部數據的引用,這個類仍(不恰當地)將其聲明為const成員函數(條目28將深入討論這個問題)。先忽略這個問題,請注意這里的operator[]實現中并沒有以任何形式修改pText。于是編譯器便會欣然接受這樣的做法,畢竟,它是按位恒定的,所有的編譯器所檢查的都是這一點。但是請觀察,在編譯器的縱容下,還會有什么樣的事情發生:

const CTextBlock cctb("Hello");   // 聲明常量對象

 

char *pc = &cctb[0];               // 調用constoperator[]

                                   // 從而得到一個指向cctb中數據的指針

 

*pc = 'J';                         // cctb現在的值為"Jello"

當你創建了一個包含具體值的對象常量后,你僅僅通過對其調用const的成員函數,就可以改變它的值!這顯然是有問題的。

邏輯恒定應運而生。堅持這一宗旨的人們爭論到:如果某個對象調用了一個const的成員函數,那么這個成員函數可以對這個對象內部做出改動,但是僅僅以客戶端無法察覺的方式進行。比如說,你的CTextBlock類可能需要保存文字塊的長度,以便在需要的時候調用:

class CTextBlock {

public:

  ...

  std::size_t length() const;

 

private:

  char *pText;

  std::size_t textLength;          // 最后一次計算出的文字塊長度

  bool lengthIsValid;              // 當前長度是否可用

};

 

std::size_t CTextBlock::length() const

{

  if (!lengthIsValid) {

    textLength = std::strlen(pText);    // 錯誤!不能在const成員函數中

    lengthIsValid = true;          // textLengthlengthIsValid賦值

  }

  return textLength;

}

以上length的實現絕不是按位恒定的。這是因為textLengthlengthIsValid都可以改動。盡管看上去它應該對于CTextBlock對象常量可用,但是編譯器會拒絕。編譯器始終堅持遵守按位恒定。那么該怎么辦呢?

解決方法很簡單:利用C++中與const相關的靈活性,使用可變的(mutable)數據成員。mutable可以使非靜態數據成員不受按位恒定規則的約束:

class CTextBlock {

public:

  ...

  std::size_t length() const;

 

private:

  char *pText;

 

  mutable std::size_t textLength;  // 這些數據成員在任何情況下均可修改

  mutable bool lengthIsValid;      // const成員函數中也可以

};

 

std::size_t CTextBlock::length() const

{

  if (!lengthIsValid) {

    textLength = std::strlen(pText);    // 現在可以修改了

    lengthIsValid = true;          // 同上

  }

  return textLength;

}

避免const與非const成員函數之間的重復

mutable對于“我不了解按位恒定”的情況不失為一個良好的解決方案,但是它并不能對于所有的const難題做到一勞永逸。舉例說,TextBlock(以及CTextBlock)中的operator[]不僅僅返回一個對恰當字符的引用,同時還要進行邊界檢查、記錄訪問信息,甚至還要進行數據完整性檢測。如果將所有這些統統放在const或非const函數(我們現在會得到過于冗長的隱式內聯函數,不過不要驚慌,在條目30中這個問題會得到解決)中,看看我們會得到什么樣的龐然大物:

class TextBlock {

public:

  ...

  const char& operator[](std::size_t position) const

  {

    ...                             // 邊界檢查

    ...                             // 記錄數據訪問信息

    ...                             // 確認數據完整性

    return text[position];

  }

  char& operator[](std::size_t position)

  {

    ...                             // 邊界檢查

    ...                             // 記錄數據訪問信息

    ...                             // 確認數據完整性

    return text[position];

  }

 

private:

   std::string text;

};

啊哦!重復代碼讓人頭疼,還有隨之而來的編譯時間增長、維護成本增加、代碼膨脹,等等……當然,像邊界檢查這一類代碼是可以移走的,它們可以單獨放在一個成員函數(很自然是私有的)中,然后讓這兩個版本的operator[]來調用它,但是你的代碼仍然有重復的函數調用,以及重復的return語句。

對于operator[]你真正需要的是:一次實現,兩次使用。也就是說,你需要一個版本的operator[]來調用另一個。這樣便可以通過轉型來消去函數的恒定性。

通常情況下轉型是一個壞主意,后邊我將專門用一條來告訴你為什么不要使用轉型(條目21),但是代碼重復也不會讓人感到有多輕松。在這種情況下,const版的operator[]與非const版的operator[]所做的事情完全相同,不同的僅僅是它的返回值是const的。通過轉型來消去返回值的恒定性是安全的,這是因為任何人調用這一非constoperator[]首先必須擁有一個非const的對象,否則它就不能調用非const函數。所以盡管需要一次轉型,在constoperator[]中調用非const版本,可以安全地避免代碼重復。下面是實例代碼,讀完后邊的文字解說你會更明了。

class TextBlock {

public:

  ...

  const char& operator[](std::size_t position) const

  {                                // 同上

    ...

    ...

    ...

    return text[position];

  }

  char& operator[](std::size_t position)

  {                                // 現在僅調用 constop[]

    return

      const_cast<char&>(           // 消去op[]返回值的const屬性

        static_cast<const TextBlock&>(*this)

                                   // *this的類型添加const屬性;

        [position];                 // 調用const版本的op[]

      );

  }

...

};

就像你所看到的,上面的代碼進行了兩次轉型,而不是一次。我們要讓非constoperator[]去調用const版本的,但是如果在非constoperator[]的內部,我們只調用operator[]而不標明const,那么函數將對自己進行遞歸調用。那將是成千上萬次的毫無意義的操作。為了避免無窮遞歸的出現,我們必須要指明我們要調用的是const版本的operator[],但是手頭并沒有直接的辦法。我們可以用*this從其原有的TextBlock&轉型到const TextBlock&。是的,我們使用了一次轉型添加了一個const!這樣我們就進行了兩次轉型:一次為*this添加了const(于是對于operator[]的調用將會正確地選擇const版本),第二次轉型消去了const operator[]返回值中的const

添加const屬性的那次轉型是為了強制保證轉換工作的安全性(從一個非const對象轉換為一個const對象),我們使用static_cast來進行。消去const的那次轉型只可以通過const_cast來完成,所以這里實際上也沒有其他的選擇。(從技術上講還是有的。C語言風格的轉型在這里也能工作,但是,就像我在條目27中所解釋的,這一類轉型在很多情況下都不是好的選擇。如果你對于static_castconst_cast還不熟悉,條目27中有相關介紹。)

在眾多的示例中,我們最終選擇了一個運算符來進行演示,因此上面的語法顯得有些古怪。這些代碼可能不會贏得任何選美比賽,但是通過以const版本的形式實現非const版本的operator[],可以避免代碼重復,這正是我們所期望的效果。為達到這一目標而寫下看似笨拙的代碼,這樣做是否值得全看你的選擇,但是,以const版本的形式來實現非const的成員函數——了解這一技術肯定是值得的。

更值得你了解的是,上面的操作是不可逆的,即:通過讓const版本的函數調用非const版本來避免代碼重復,是不可行的。請記住,一個const成員函數應保證永遠不會更改其所在對象的邏輯狀態,但是一個非const的成員函數無法做出這樣的保證。如果你在一個const函數中調用了一個非const函數,那么你曾保證不會被改動的對象就有被修改的風險。這就是為什么說讓一個const成員函數調用一個非const成員函數是錯誤的:對象有可能被修改。實際上,為了使代碼能夠得到編譯,你還需要使用一個const_cast來消去*thisconst屬性,顯然這是不必要的麻煩。上一段中按相反的調用次序才是安全的:非const成員函數可以對一個對象做任何想做的事情,因此調用一個const成員函數不會帶來任何風險。這就是為什么static_cast可以這樣操作*this的原因:這里不存在const相關的危險。

就像本條目一開始所說的,const是一個令人贊嘆的東西。對于指針和迭代器,以及對于指針、迭代器和引用所涉及的對象,對于函數的參數和返回值,對于局部變量,以及對于成員函數來說,const都是一個強大的伙伴。盡可能去利用它。你一定不會后悔。

時刻牢記

將一些東西聲明為const可以幫助編譯器及時發現用法上的錯誤。const可以用于各個領域,包括任意作用域的對象、函數參數和返回值、成員函數。

編譯器嚴格遵守按位恒定規則,但是你應該在需要時應用邏輯恒定。

當const和非const成員函數的實現在本質上相同時,可以通過使用一個非const版本來調用const版本來避免代碼重復。

posted on 2007-04-11 19:55 ★ROY★ 閱讀(1453) 評論(3)  編輯 收藏 引用 所屬分類: Effective C++

評論

# re: 【翻譯】Effective C++ (第3項:盡可能使用const)  回復  更多評論   

哈哈 ,我最近在看這本書,蠻好的,但我感覺有的準則不實用,可能是我代碼寫太少了,沒有用到吧!
2007-04-13 16:18 | 攀升

# re: 【翻譯】Effective C++ (第3項:盡可能使用const)  回復  更多評論   

請問這個翻譯是干嘛用的?博主自己翻譯的嗎?不是已經有中文版了嗎?
2007-04-24 17:49 | 匿名

# re: 【翻譯】Effective C++ (第3項:盡可能使用const)  回復  更多評論   

@匿名
向領導匯報:
翻著玩的,自己學習。
是自己翻譯的。
是有中文版了,但是我們干的好多事都是別人干過的,不是嗎?
2007-04-24 17:55 | ★田德健★
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲美女视频| 一本色道久久综合亚洲精品高清 | 日韩一本二本av| 在线电影国产精品| 亚洲国产精品久久久| 91久久精品美女高潮| 亚洲美女视频网| 一区二区日韩伦理片| 亚洲影视在线| 欧美中文在线观看国产| 性欧美在线看片a免费观看| 久久激情五月婷婷| 免费看亚洲片| 最新国产の精品合集bt伙计| 久久精品一区二区三区不卡牛牛| 久久天天狠狠| 亚洲国产精品一区二区www| 亚洲伦理自拍| 亚洲女女女同性video| 久久久91精品| 欧美日韩国产va另类| 国产精品久久77777| 国产综合色产在线精品| 亚洲高清视频一区二区| 亚洲午夜精品久久久久久app| 欧美一级久久久| 免费看精品久久片| 一区二区不卡在线视频 午夜欧美不卡在 | 国产精品爱啪在线线免费观看 | 一本色道久久综合精品竹菊| 亚洲男人的天堂在线aⅴ视频| 久久久久久久久久久久久久一区| 欧美激情片在线观看| 一区二区高清| 久久三级福利| 国产精品成人va在线观看| 国内成+人亚洲+欧美+综合在线| 亚洲人永久免费| 久久精品国产999大香线蕉| 亚洲一区二区综合| 国产一在线精品一区在线观看| 最新日韩av| 久久爱另类一区二区小说| 亚洲国产三级在线| 亚洲欧美日韩中文播放| 欧美国产日韩亚洲一区| 国产一区99| 亚洲网站啪啪| 亚洲国产欧美久久| 欧美在线视频a| 欧美日韩国产成人精品| 亚洲福利精品| 久久综合激情| 欧美一级成年大片在线观看| 欧美日韩三级| 亚洲伦理在线免费看| 久久综合免费视频影院| 亚洲欧美一区二区三区久久| 欧美新色视频| 亚洲在线观看免费视频| 日韩视频一区二区在线观看| 欧美高清自拍一区| 亚洲国产婷婷综合在线精品| 免费成人小视频| 欧美一区二区三区四区在线观看 | 国产亚洲欧美日韩一区二区| 亚洲欧美日本在线| 一本色道久久88综合日韩精品| 欧美区视频在线观看| 亚洲乱码国产乱码精品精天堂| 欧美高清视频一区二区三区在线观看| 欧美在现视频| 经典三级久久| 欧美二区乱c少妇| 免费成人黄色片| 亚洲区一区二| 亚洲精选在线观看| 欧美人在线观看| 亚洲午夜电影网| 亚洲图片欧洲图片av| 国产精品你懂的在线欣赏| 销魂美女一区二区三区视频在线| 亚洲视频免费在线| 国产精品亚洲欧美| 久久久www成人免费精品| 欧美一区二区在线免费观看 | 亚洲综合日韩| 亚洲欧美日韩一区二区三区在线观看 | 亚洲欧洲日本一区二区三区| 欧美日韩精品一区视频| 亚洲女同在线| 欧美一区二区视频在线观看2020| 在线看一区二区| 亚洲人成网站777色婷婷| 欧美在线观看一二区| 在线观看欧美| 亚洲日本精品国产第一区| 欧美体内she精视频在线观看| 欧美一区1区三区3区公司| 久久精品伊人| 一本色道精品久久一区二区三区| 亚洲免费综合| 亚洲欧洲精品一区二区三区 | 久久精品国产清自在天天线 | 久久精品国产77777蜜臀| 激情久久婷婷| 亚洲免费成人av| 国产精品影视天天线| 欧美国产日韩免费| 欧美日韩在线高清| 美脚丝袜一区二区三区在线观看 | 久久综合亚洲社区| 亚洲视屏在线播放| 久久婷婷国产综合精品青草| 亚洲网站视频| 猛干欧美女孩| 久久av资源网| 欧美日韩少妇| 欧美好骚综合网| 国产在线一区二区三区四区| 亚洲精品激情| 亚洲春色另类小说| 亚洲欧美精品suv| aa级大片欧美| 久久婷婷久久| 久久se精品一区精品二区| 欧美日韩爆操| 欧美成人精品高清在线播放| 国产偷久久久精品专区| 亚洲桃色在线一区| 亚洲成人原创| 黄色在线一区| 99国产精品私拍| 亚洲国产日韩美| 久久久999成人| 欧美一区激情| 国产精品视频区| 亚洲九九爱视频| 一区二区高清| 欧美激情一区二区三区在线视频观看| 美女主播精品视频一二三四| 国产在线高清精品| 午夜精品久久久久久久久久久久| 午夜精品一区二区三区在线播放 | 欧美亚洲一区二区在线| 欧美日韩视频在线一区二区| 亚洲精品视频在线播放| 日韩亚洲欧美成人一区| 欧美第一黄色网| 欧美激情一区二区三区高清视频| 欧美精品久久一区二区| 美女国产一区| 亚洲第一福利视频| 猫咪成人在线观看| 欧美黄色大片网站| 亚洲精品免费电影| 欧美另类在线播放| 一区二区三区日韩欧美精品| 亚洲一区网站| 国产欧美一区视频| 久久动漫亚洲| 麻豆精品视频在线观看| 91久久精品www人人做人人爽| 欧美国产激情二区三区| 99www免费人成精品| 亚洲欧美日韩国产另类专区| 国产欧美va欧美不卡在线| 久久久一本精品99久久精品66| 亚洲高清不卡| 亚洲午夜电影在线观看| 国产偷自视频区视频一区二区| 久久在线播放| 亚洲免费观看在线视频| 欧美一区影院| 亚洲国产婷婷综合在线精品| 欧美小视频在线| 久久这里只精品最新地址| 亚洲精品乱码久久久久久| 午夜精品视频| 亚洲国产99| 国产精品乱码人人做人人爱| 久久久女女女女999久久| 99re6热在线精品视频播放速度 | 国产一区二区三区日韩欧美| 欧美国产日本韩| 香蕉精品999视频一区二区| 欧美激情第二页| 亚洲免费小视频| 亚洲激情一区二区| 国产精品视频| 欧美国产视频在线| 午夜亚洲福利| 99视频超级精品| 欧美国产一区二区| 欧美在线国产| 一区二区激情| 亚洲黄页视频免费观看| 国产欧美精品一区| 欧美视频一区二区三区在线观看| 久久婷婷激情|