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

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

Effective C++ - Accustoming Yourself to C++

2019-11-14 09:54:11
字體:
來源:轉載
供稿:網友

Effective C++ - Accustoming Yourself to C++

前言:如何有效運用C++,包括一般性的設計策略,以及帶有具體細節的特定語言特性。知道細節很重要,否則如果疏忽幾乎總是導致不可預期的程序行為(undefined behavior)。本文總結對于如何使用C++的一些建議,從而讓你成為一個有戰斗力的C++程序員。


Effective C - Accustoming Yourself to C構造函數的explicit對象的復制命名習慣TR1和Boost視C為一個語言聯邦盡量以const enum inline替換define盡量使用const確定對象被使用前已先被初始化

1 構造函數的explicit

被聲明為explicit的構造函數通常比non-explicit更受歡迎,因為它們禁止編譯器執行非預期的類型轉換。除非有一個好理由允許構造函數被用于隱式類型轉換,否則把它聲明為explicit。

class foo {public: explicit foo(int x);};

2 對象的復制

copy構造函數被用來“以同型對象初始化自我對象”,copy assignment操作符被用來“從另一個同型對象中拷貝其值到自我對象”。

class Widget {public: Widget(); // default構造函數 Widget(const Widget& rhs); // copy構造函數 Widget& Operator=(const Widget& rhs); // copy assignment操作符};Widget w1; // 調用default構造函數Widget w2(w1); // 調用copy構造函數w1 = w2; // 調用copy assignment操作符Widget w3 = w2; // 調用copy構造函數

copy構造和copy賦值的區別:如果一個新對象被定義,一定會有一個構造函數被調用,不可能調用賦值操作。如果沒有新對象被定義,就不會有構造函數被調用,那么就是賦值操作被調用。

3 命名習慣

構造函數和析構函數分別使用縮寫ctordtor代替。 使用lhs(left-hand side)和rhs(right-hand side)表示參數名稱。

4 TR1和Boost

TR1(Technical Report 1)是一份規范,描述加入C++標準程序庫的諸多新機能。這些機能以新的class templatesfunction templates形式體現。所有TR1組件都被置于命名空間tr1內。 Boost是個組織,亦是一個網站,提供可移植,源代碼開放的C++程序庫。大多數TR1機能是以Boost的工作為基礎。

5 視C++為一個語言聯邦

今天的C++已經是個多重范型編程語言(multiparadigm PRogramming language),一個同時支持以下特性的語言: * 過程形式(procedural) * 面向對象形式(object-oriented) * 函數形式(functional) * 泛型形式(generic) * 元編程形式(metaprogramming)

為了理解C++,你必須認識其主要的次語言(sublanguage)

C 說到底C++仍是以C為基礎。blocks, statements, preprocessor, built-in data types, arrays, pointers等統統來自C。許多時候C++對問題的解法其實不過就是較高級的C解法,但是C++提供了C沒有的templates, exceptions, overloading(重載)等功能。

C語言可以重載嗎

// http://www.cplusplus.com/reference/cstdlib/qsort//* qsort example */#include <stdio.h> /* printf */#include <stdlib.h> /* qsort */int values[] = { 40, 10, 100, 90, 20, 25 };int compare (const void * a, const void * b){ return ( *(int*)a - *(int*)b );}void fun(){ printf("fun()/n");}/*$gcc -o overload_test overload_test.c overload_test.c:18:6: error: redefinition of 'fun'void fun(int a) ^overload_test.c:13:6: note: previous definition is herevoid fun() ^1 error generated. */#if 0void fun(int a){ printf("fun(int a)/n");}#endifint main (){ // 測試C語言是否支持overload重載 fun(); // C語言可以通過不同的函數指針來模擬overload重載 int n; qsort (values, 6, sizeof(int), compare); for (n=0; n<6; n++) printf ("%d ",values[n]); return 0;}

Object-Oriented C++ 這部分就是C with Classes所訴求的:

classes(包括構造函數和析構函數)encapsulation(封裝)inheritance(繼承)polymorphism(多態)virtual function(虛函數動態綁定)etc.

Template C++ 這是C++的泛型編程(generic programming)部分,也是大多數程序員經驗最少的部分。

STL STL是個template程序庫,它對containers, iterators, algorithms以及function objects的規約有極佳的緊密配合與協調。

6 盡量以const, enum, inline替換#define

寧可以編譯器替換預處理器。當你做出這樣的事情:

#define aspECT_RATIO 1.653

記號名稱ASPECT_RATIO也許從未被編譯器看見,也許在編譯器開始處理源碼之前就被預處理器替換了,于是記號名稱有可能沒有進入記號表(symbol table)內,當你運用此常量但獲得一個編譯錯誤時可能會帶來困惑,因為這個錯誤信息提到的是1.653而不是ASPECT_RATIO。尤其是如果ASPECT_RATIO被定義在一個非你所寫的頭文件內,你肯定對1.653來自何處毫無概念。解決的方法是:以一個常量替換上述的宏(#define)。

const double AspectRatio = 1.653; // 大寫名稱通常用于宏

好處是:

作為一個語言常量,AspectRatio肯定會被編譯器看到,當然就會進入記號表內。使用常量可能比使用#define導致較小量的目標代碼,因為預處理器盲目地將宏名稱進行替換會導致目標代碼出現多份1.653,而若改用常量則不會出現。字符串常量,string對象通常比char*-based合適。const char* const authorName = "gerry";const std::string authorName("gerry");class專屬常量。為了將常量的作用域(scope)限制在class內,你必須讓它成為class的一個成員(member),另外為了保證此常量至多只有一份實體,必須讓它成為一個static成員。#include<stdio.h>class GamePlayer {public: void set_scores() { for (int i = 0; i != NumTurns; ++i) { scores[i] = i; } } void get_scores() { for (int i = 0; i != NumTurns; ++i) { printf("%d ", scores[i]); } printf("/n"); } static int get_numturns() { //printf("addr GamePlayer::NumTurns[%p]/n", &GamePlayer::NumTurns); return GamePlayer::NumTurns; }private: static const int NumTurns = 5; // 常量聲明 int scores[NumTurns]; // 使用該常量};int main(){ printf("GamePlayer::NumTurns[%d]/n", GamePlayer::get_numturns()); GamePlayer player; player.set_scores(); player.get_scores(); GamePlayer player2; printf("player.NumTurns[%d] player2.NumTurns[%d]/n", player.get_numturns(), player2.get_numturns()); return 0;}/*GamePlayer::NumTurns[5]0 1 2 3 4 player.NumTurns[5] player2.NumTurns[5] */

然而,上面你所看到的是NumTurns的聲明式,而非定義式。通常C++要求所使用的任何東西提供一個定義式,但如果它是class專屬常量且又是static整數類型,只要不取它們的地址,你可以聲明并使用它們而無須提供定義式。

但是,如果你需要取某個class專屬常量的地址,或者編譯器要求(比如,老編譯器)需要看到一個定義式,那么需要另外提供定義式

#include<stdio.h>class GamePlayer {public: void set_scores() { for (int i = 0; i != NumTurns; ++i) { scores[i] = i; } } void get_scores() { for (int i = 0; i != NumTurns; ++i) { printf("%d ", scores[i]); } printf("/n"); } static int get_numturns() { printf("addr GamePlayer::NumTurns[%p]/n", &GamePlayer::NumTurns); return GamePlayer::NumTurns; }private: static const int NumTurns = 5; // 常量聲明 int scores[NumTurns]; // 使用該常量};const int GamePlayer::NumTurns; // NumTurns的定義int main(){ printf("GamePlayer::NumTurns[%d]/n", GamePlayer::get_numturns()); GamePlayer player; player.set_scores(); player.get_scores(); GamePlayer player2; printf("player.NumTurns[%d] player2.NumTurns[%d]/n", player.get_numturns(), player2.get_numturns()); return 0;}/*addr GamePlayer::NumTurns[0x102092f30]GamePlayer::NumTurns[5]0 1 2 3 4 addr GamePlayer::NumTurns[0x102092f30]addr GamePlayer::NumTurns[0x102092f30]player.NumTurns[5] player2.NumTurns[5]*/

通過提供定義式,我們就可以獲取class專屬常量的地址。

注意:

NumTurns的定義式中沒有賦值是因為,class常量已在聲明時獲得了初值,因此定義時不可以再設置初值。 我們無法利用#define創建一個class專屬常量,因為#define并不能限制作用域(scope),一旦宏被定義,它就在其后的編譯過程中有效,除非在某處被#undef。因此,#define不僅不能用來定義class專屬常量,也不能提供任何封裝性。

如果想具備作用域,但又不想取地址,可以使用enum來實現這個約束。

class GamePlayer {public: void set_scores() { for (int i = 0; i != NumTurns; ++i) { scores[i] = i; } } void get_scores() { for (int i = 0; i != NumTurns; ++i) { printf("%d ", scores[i]); } printf("/n"); } static int get_numturns() { //printf("addr GamePlayer::NumTurns[%p]/n", &GamePlayer::NumTurns); return GamePlayer::NumTurns; }private: //static const int NumTurns = 5; // 常量聲明 enum { NumTurns = 5, // 令NumTurns成為5的一個記號名稱 }; int scores[NumTurns]; // 使用該常量};

預處理器和宏的陷阱:

宏看起來像函數,但是不會招致函數調用(function call)帶來的額外開銷。 糟糕的做法:(有效率,但不安全)

// 以a和b的較大值調用f函數#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

好的做法:(效率和安全同時得到保證)

template<typename T>inline void callWithMax(const T& a, const T& b){ f(a > b ? a : b);}

這個template根據實例化可以產出一整群函數,每個函數都接受兩個同類型對象,并以其中較大的調用f。這里不需要在函數本體中為參數加上括號,也不需要操心參數被計算的次數,同時,由于callWithMax是個真正的函數,它遵守作用域和訪問規則,因此可以寫出一個class內的private inline函數,而對于宏是無法完成的。

請記?。?/p> 對于單純常量,最好以const對象或enum替換#define 對于形似函數的宏,最好改用inline函數替換#define

7 盡量使用const

const允許你指定一個語義約束,也就是指定一個“不該被改動”的對象,而編譯器會強制實施該項約束。

char greeting[] = "Hello";char* p = greeting; // non-const pointer, non-const dataconst char* p = greeting; // non-const pointer, const datachar* const p = greeting; // const pointer, non-const dataconst char* const p = greeting; // const pointer, const data

如果關鍵字const出現在星號左邊,表示被指物是常量;如果出現在星號右邊,表示指針自身是常量;如果出現在星號兩邊,表示被指物和指針兩者都是常量。

注意:如果被指物是常量,將關鍵字const寫在類型之前,和寫在類型之后星號之前,這兩種寫法的意義相同。

void f1(const Widget* pw);void f2(Widget const * pw);

STL迭代器系以指針為根據塑模出來,所以迭代器的作用就像個T*指針。如果你希望迭代器所指的東西不可被改變,則需要使用const_iterator。

std::vector<int> vec;const std::vector<int>::iterator iter = vec.begin();*iter = 10; // ok++iter; // errorstd::vector<int>::const_iterator citer = vec.begin();*citer = 10; // error++citer; // ok

const成員函數

const實施于成員函數的目的,是為了確認該成員函數可作用于const對象身上。這一類成員函數之所以重要,是因為:

它們使class接口比較容易被理解,可以得知哪個函數可以改動對象內容,而哪個函數不行。它們使“操作const對象”成為可能,這對編寫高效代碼是個關鍵,比如,改善程序效率的一個根本方法是以pass by reference-to-const方式傳遞對象,而此技術可行的前提是,我們有const成員函數可用來處理取得的const對象。

注意:兩個成員函數如果只是常量性不同,可以被重載(overload)。只有返回值類型不同的兩個函數不能重載(functions that differ only in their return type cannot be overloaded)。

#include<stdio.h>#include<iostream>#include<string>class TextBlock {public: TextBlock() { } TextBlock(const char* lhs) { text = lhs; }public: // operator[] for const object const char& operator[] (std::size_t position) const { return text[position]; } // operator[] for non-const object char& operator[] (std::size_t position) { return text[position]; }private: std::string text;};int main(){ TextBlock tb("gerry"); std::cout << tb[0] << std::endl; // 調用non-const TextBlock::operator[] const TextBlock ctb("yang"); // 調用const TextBlock::operator[] std::cout << ctb[0] << std::endl; return 0;}

成員函數如果是const意味著什么?—— bitwise constness或者physical constness VS logical constness

bitwise const指的是,成員函數只有在不更改對象之任何成員變量(static除外)時才可以說是const,即,const成員函數不可以更改對象內任何non-static成員變量。

注意:許多成員函數雖然不完全具備const性質,卻能通過bitwise測試。比如,一個更改了”指針所指物”的成員函數,如果只有指針隸屬于對象,那么此函數為bitwise const不會引發編譯器異議,但是實際不能算是const。

下面這段代碼,可以通過bitwise測試,但是實際上改變了對象的值。

#include<stdio.h>#include<iostream>#include<string>class TextBlock {public: TextBlock() { } TextBlock(char* lhs) { pText = lhs; }public: // operator[] for const object char& operator[] (std::size_t position) const { return pText[position]; }#if 0 // operator[] for non-const object char& operator[] (std::size_t position) { return pText[position]; }#endifprivate: char* pText;};int main(){ char name[] = "gerry"; const TextBlock ctb(name); std::cout << ctb[0] << std::endl; // 調用const TextBlock::operator[] char* pc = &ctb[0]; *pc = 'J'; std::cout << ctb[0] << std::endl; // 調用const TextBlock::operator[] return 0;}

logical constness主張,一個const成員函數可以修改它所處理的對象的某些bits,但只有在客戶端偵測不出的情況才可以(即,對客戶端是透明的,但是實際上對象的某些值允許改變)。正常情況下,由于bitwise const的約束,const成員函數內是不允許修改non-static成員變量的,但是通過將一些變量聲明為mutable則可以躲過編譯器的bitwise const約束。

#include<stdio.h>#include<iostream>#include<string>#include<string.h>class TextBlock {public: TextBlock() : lengthIsValid(false) { } TextBlock(char* lhs) : lengthIsValid(false) { pText = lhs; }public: std::size_t length() const { if (!lengthIsValid) { printf("do strlen... "); textLength = std::strlen(pText); // error? 在const成員函數內不能修改non-static成員變量 lengthIsValid = true; // 同上 } return textLength; } // operator[] for const object char& operator[] (std::size_t position) const { return pText[position]; }#if 0 // operator[] for non-const object char& operator[] (std::size_t position) { return pText[position]; }#endifprivate: char* pText; mutable std::size_t textLength; // 最近一次計算的文本區域塊長度 mutable bool lengthIsValid; // 目前的長度是否有效};int main(){ char name[] = "gerry"; const TextBlock ctb(name); std::cout << ctb[0] << std::endl; // 調用const TextBlock::operator[] std::cout << "length: " << ctb.length() << std::endl; char* pc = &ctb[0]; *pc = 'J'; std::cout << ctb[0] << std::endl; // 調用const TextBlock::operator[] std::cout << "length: " << ctb.length() << std::endl; return 0;}/*$./mutable glength: do strlen... 5Jlength: 5 */

constnon-const成員函數中避免重復

方法是:運用const成員函數實現出其non-const孿生兄弟。

不好的做法(因為有重復代碼):

// operator[] for const object const char& operator[] (std::size_t position) const { // bounds checking // log access data // verify data integrity // ... return text[position]; } // operator[] for non-const object char& operator[] (std::size_t position) { // bounds checking // log access data // verify data integrity // ... return text[position]; }

好的做法(實現operator[]的機能一次并使用它兩次,令其中一個調用另一個):

#include<stdio.h>#include<iostream>#include<string>class TextBlock {public: TextBlock() { } TextBlock(const char* lhs) { text = lhs; }public: // operator[] for const object const char& operator[] (std::size_t position) const { // bounds checking // log access data // verify data integrity // ... std::cout << "const char& operator[]() const/n"; return text[position]; }#if 0 // operator[] for non-const object char& operator[] (std::size_t position) { // bounds checking // log access data // verify data integrity // ... return text[position]; }#endif char& operator[] (std::size_t position) { std::cout << "char& operator[]()/n"; return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]); }private: std::string text;};int main(){ TextBlock tb("gerry"); std::cout << tb[0] << std::endl; // 調用non-const TextBlock::operator[] const TextBlock ctb("yang"); // 調用const TextBlock::operator[] std::cout << ctb[0] << std::endl; return 0;}/*char& operator[]()const char& operator[]() constgconst char& operator[]() consty */

請記?。?/p> 將某些東西聲明為const可幫助編譯器偵測出錯誤用法。const可被施加于任何作用域內的對象、函數參數、函數返回類型、成員函數本體。 編譯器強制實施bitwise constness,但你編寫程序時應該使用“概念上的常量性”。 當constnon-const成員函數有著實質等價的實現時,令non-const版本調用const版本可避免代碼重復。

8 確定對象被使用前已先被初始化

關于“將對象初始化”這事,C++似乎反復無常(對象的初始化動作何時一定發生,何時不一定發生)。針對這種復雜的規則,最佳的處理方法是:永遠在使用對象之前先將它初始化

對于內置類型,必須手工完成初始化;對于內置類型以外的其他類型,初始化責任落在構造函數(constructors)身上,即,確保每一個構造函數都將對象的每一個成員初始化。

構造函數初始化的正確方法是:使用member initialization list(成員初值列),而不是在構造函數中的賦值。因為第一種方法的執行效率通常較高(對于大多數類型而言,比起先調用default構造函數,然后再調用copy assignment操作符,單只調用一次copy構造函數是比較高效的。對于內置類型,其初始化和賦值的成本相同,但為了一致性最好也通過成員初值列來初始化)。

ABEntry:ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones): theName(name), // 成員初值列表,這些都是初始化 theAddress(address), thePhones(phones), numTimesConsulted(0){ }ABEntry::ABEntry(): theName(), // 調用theName的`default`構造函數 theAddress(), // 同上 thePhones(), // 同上 numTimesConsulted(0) // 將內置類型int顯示初始化為0{ }

C++有著十分固定的”成員初始化次序”:總是base classes更早于其derived classes被初始化。而class的成員變量總是以其聲明次序被初始化,而和它們在成員初始值列中的出現次序無關。建議,當你在成員初值列中初始化各個成員時,最好總是和其聲明的次序一致。

最后一個問題:不同編譯單元內定義的non-local static對象的初始化順序是怎么樣的?

函數內的static對象稱為local static對象,其他static對象稱為non-local static對象。

C++對定義于不同編譯單元內的non-local static對象的初始化次序并無明確定義。因此,如果某編譯單元內的某個non-local static對象的初始化動作依賴另一編譯單元內的某個non-local static對象,那么它所用到的這個對象可能尚未被初始化。

針對上面這個問題的解決方法是: 將每個non-local static對象搬到自己的專屬函數內,這些函數返回一個reference指向它所含的對象。即,non-local static對象被local static對象替換了。

class FileSystem { ... };FileSystem& tfs(){ static FileSystem fs; return fs;}

注意:這些函數內含static對象的事實使它們在多線程系統中帶有不確定性。處理這種麻煩的方法是,在程序的單線程啟動階段,手工調用所有reference-returning函數,這可消除與初始化有關的race conditions(競速形勢)。

請記住

為內置類型對象進行手工初始化,因為C++不保證初始化它們。 構造函數最好使用成員初值列(member initialization list),而不要在構造函數本體內使用賦值操作(assignment)。初值列列出的成員變量,其排列次序應該和它們在class中的聲明次序相同。 為免除跨編譯單元的初始化次序問題,請以local static對象替換non-local static對象。

下一篇: Effective C++ - Constructors, Destructors, and Assignment Operators


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色综合视频一区中文字幕| 欧美日韩加勒比精品一区| 中文字幕日韩在线视频| 亚洲va欧美va在线观看| 国产99久久精品一区二区| 精品成人69xx.xyz| 日韩中文在线中文网三级| 日韩一区二区福利| 欧美另类交人妖| 亚洲在线免费观看| 日韩欧美在线字幕| 国产91色在线免费| 欧美性xxxx| 中文字幕在线精品| 91精品久久久久久久久| 国产网站欧美日韩免费精品在线观看| 午夜精品久久久久久久久久久久久| 亚洲国产精品国自产拍av秋霞| 伊人伊成久久人综合网小说| 成人国产精品一区| 欧美日韩精品中文字幕| 91香蕉嫩草影院入口| 欧美激情国产高清| 亚洲日本中文字幕免费在线不卡| 日韩电影网在线| 国产精品自拍视频| 欧美中文在线字幕| 日韩中文字幕视频在线观看| 亚洲影院在线看| 精品久久久久久电影| 国产精品国产三级国产aⅴ浪潮| 理论片在线不卡免费观看| 97精品国产97久久久久久春色| 性欧美长视频免费观看不卡| 欧美精品精品精品精品免费| 国产91露脸中文字幕在线| 久久亚洲精品毛片| 日韩av一区二区在线观看| 伊人久久大香线蕉av一区二区| 国产精品国语对白| 97国产精品视频人人做人人爱| 亚洲精品国产精品国自产在线| 国产丝袜一区二区三区| 久99九色视频在线观看| 欧美午夜www高清视频| 欧美午夜精品在线| 日韩美女在线观看一区| 91成人天堂久久成人| xx视频.9999.com| 国产91av在线| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美日韩免费观看中文| 欧美福利视频网站| 热99精品只有里视频精品| 国产精品日韩在线播放| 亚洲欧美日韩一区二区三区在线| 亚洲综合av影视| 欧美在线精品免播放器视频| 日韩精品极品视频| 国产精品一区二区久久久久| 国产精品视频一| 成人免费视频a| 日本精品va在线观看| 国产精品久久久久久久电影| 国产视频精品va久久久久久| 精品久久中文字幕| 国产热re99久久6国产精品| 97在线视频一区| 欧美精品成人在线| 欧美激情精品久久久| 久久久久久久香蕉网| 亚洲视频日韩精品| 国产91对白在线播放| 91成人天堂久久成人| 7777精品久久久久久| 国产日产亚洲精品| 日韩精品电影网| 久久久影视精品| 亚洲bt欧美bt日本bt| 国产精品久久久久久久久久小说| 午夜精品视频在线| 国产日韩精品在线观看| 欧美精品激情在线| 国产精品久久久久久av福利软件| 亚洲精品美女在线| 欧美国产日韩一区二区在线观看| 亚洲国产天堂网精品网站| 在线精品高清中文字幕| 国产一区二区在线免费| 国产热re99久久6国产精品| 2018中文字幕一区二区三区| 海角国产乱辈乱精品视频| 欧美日韩亚洲一区二区| 国产精品2018| 欧美中文在线视频| 亚洲一区二区三区久久| 草民午夜欧美限制a级福利片| 国产精品日韩欧美| 欧美精品在线看| 热久久视久久精品18亚洲精品| 伊人青青综合网站| 亚洲国产精品久久久久久| 国产精品wwww| 欧美做受高潮电影o| 国产精品一区二区三区免费视频| 亚洲乱亚洲乱妇无码| 国产日韩欧美日韩大片| 粉嫩av一区二区三区免费野| 国产精品十八以下禁看| 日韩电影免费在线观看中文字幕| 日韩亚洲欧美成人| 欧美亚洲另类在线| 中国日韩欧美久久久久久久久| 欧美成人精品在线播放| 9.1国产丝袜在线观看| 日韩av电影手机在线| 日韩电影免费在线观看| 6080yy精品一区二区三区| 欧美亚洲成人网| 欧美精品久久久久a| 久久韩国免费视频| 91在线观看免费高清| 欧美日韩中文字幕日韩欧美| 亚洲成人激情小说| 国产精品偷伦免费视频观看的| 亚洲欧美在线免费观看| 日韩欧美国产视频| 国产视频欧美视频| 一区二区三区黄色| 欧美中文在线观看国产| 亚洲精品98久久久久久中文字幕| 88国产精品欧美一区二区三区| 国产69精品99久久久久久宅男| 九九精品在线播放| 欧美大片欧美激情性色a∨久久| 欧美日韩国产丝袜美女| 另类天堂视频在线观看| 欧美理论在线观看| 色无极影院亚洲| 海角国产乱辈乱精品视频| 尤物yw午夜国产精品视频| 国产91精品黑色丝袜高跟鞋| 97不卡在线视频| 成人在线免费观看视视频| 久久久精品中文字幕| 国产日韩在线精品av| 91人人爽人人爽人人精88v| 成人国产精品色哟哟| 亚洲成色999久久网站| 国产精彩精品视频| 日韩精品视频免费专区在线播放| 欧美性xxxxhd| 国产午夜精品久久久| 这里只有视频精品| 亚洲国产精品va在看黑人| 91av在线精品| 色综合男人天堂| 国产成人av在线播放| 欧美性生活大片免费观看网址| 精品国产91久久久久久老师| 久久影视电视剧免费网站| 91日韩在线视频| 亚洲精品xxx| 国产视频观看一区|