<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    PS,1880后程序員

    看不完的牙,寫不完的程序,跑不完的步。
    隨筆 - 97, 文章 - 34, 評論 - 10, 引用 - 0
    數據加載中……

    C++ Primer 之 讀書筆記 第十五章 面向對象編程

     

    Chap 15 Object-Oriented Programming

    15.1 OOP總覽

    繼承

    inheritance hierarchy

    動態綁定

    C++里,當虛函數通過基類的引用或者指針被調用時,就會發生動態綁定。(In C++, dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class.

    15.2 Defining Base and Derived Classes

    15.2.1定義基類(Defining a Base Class

    基類經常定義函數為virtual,這樣派生類需要對這樣的函數重新定義。(A base class usually should define as virtual any function that a derived class will need to redefine.

    缺省情況下,函數都是非虛函數。非虛函數的調用是在編譯時確定的。(By default, member functions are nonvirtual. Calls to nonvirtual functions are resolved at compile time.

    15.2.2protected 成員(protected Members

    protected成員

    • 不能被類的使用者來訪問,這像private member
    • 能夠被派生類訪問,這像public member  

    派生類要通過派生對象來訪問基類里的protected成員;對于基類類型的對象的protected成員,派生類沒有特殊的訪問權限。

    void Bulk_item::memfcn(const Bulk_item &d, const Item_base &b)

    {

        // attempt to use protected member

        double ret = price;   // ok: uses this->price

        ret = d.price; // ok: uses price from a Bulk_item object

        ret = b.price; // error: no access to price from an Item_base

    }

    因為Bulk_item沒有對Item_base對象的特殊的訪問權限。所以ret = b.price;錯誤。但是ret = d.price;是對的。

    類設計和protected member

    僅僅對一個沒有派生類的類來說,只有

    public:類用戶可以訪問。

    private:類成員和友元可以訪問。

    有了繼承,就有了protected

    protected:派生類運行訪問。

    15.2.3派生類(Derived Classes

    定義派生類   

    class classname: access-label base-class

    解訪問標號決定了對繼承成員的訪問權限(the access label determines the access to the inherited members.?)

    虛函數返回值是基類本身的引用或者指針

    派生類里的虛函數可以返回類引用或者是類指針。這個類是基類函數的返回值的派生類。(virtual function in a derived class can return a reference (or pointer) to a class that is publicly derived from the type returned by the base-class function.

    派生類可以訪問基類的publicprotected的成員。(classes may access the public and protected members of its base class

    類必須在它用作其它類的基類前定義。(A class must be defined before it can be used as a base class.)注意啦,這里是定義,而不是聲明哦。

    派生類的聲明不必包含它的派生列表。(If we need to declare a derived class, the declaration contains the class name but does not include its derivation list.

    // forward declarations of both derived and nonderived class

    class Bulk_item;

    class Item_base;

    15.2.4virtual和其他成員函數(virtual and Other Member Functions

    動態綁定有兩個觸發條件:

    1. 成員函數必須是虛函數

    2.    通過基類類型的指針或者引用產生的調用。就是說如果是通過對象來調用虛函數也不會產生動態綁定嘍。

    這是因為基類類型指針即可以指向基類對象也可以指向派生類對象,只有在運行期(run-time),才能夠知曉指針指向的何種類型的對象。

    基類類型的引用和指針可以分成:

    靜態類型static type:在編譯期就可以確定引用和指針的類型

    動態類型dynamic type:在運行期才能確定引用和指針綁定的對象類型。

    這兩種類型可能不同。

    在運行期才能確定虛函數的調用

    c++動態綁定的關鍵是對象的實際類型也許不同于指針或引用的靜態類型。(The fact that the actual type of the object might differ from the static type of the reference or pointer addressing that object is the key to dynamic binding in C++.

    當虛函數通過引用或者指針調用時,編譯器生成代碼來決定在運行期調用哪個函數。(When a virtual function is called through a reference or pointer, the compiler generates code to decide at run time which function to call.

    // member binary operator: left-hand operand bound to implicit this pointer

    非虛函數調用是在編譯期確定的

    下面的例子里,即使Bulk_item定義了自己的book函數,也是不會被調用的,還是要調用基類的book函數,因為book函數不是虛函數。這和Java有些不同。

    // calculate and print price for given number of copies, applying any discounts

    void print_total(ostream &os,

                     const Item_base &item, size_t n)

    {

        os << "ISBN: " << item.book() // calls Item_base::book

           << ""tnumber sold: " << n << ""ttotal price: "

           // virtual call: which version of net_price to call is resolved at run time

           << item.net_price(n) << endl;

    }

    //calling

    Item_base base;

    Bulk_item derived;

    // print_total makes a virtual call to net_price

    print_total(cout, base, 10);     // calls Item_base::net_price; Item_base::book

    print_total(cout, derived, 10); // calls Bulk_item::net_price; Item_base::book

    重寫虛函數

    似乎永遠也記不住的名詞:(順手再抄一遍)L

    override表示“重寫”,用于繼承一個基類的時候,基類當中虛擬成員的實現。

    overload表示“重載”,用于同一類中同名方法但參數個數或類型不同的實現,也就是讓方法有不同簽名的版本。

    在派生類里,虛函數一般希望可以執行基類的操作的同時執行自己特有的操作,因此需要顯示調用基類的虛函數,通過使用范圍操作符來指定調用基類的虛函數,就是這樣:

    Item_base *baseP = &derived;

    // calls version from the base class regardless of the dynamic type of baseP

    double d = baseP->Item_base::net_price(42);

    虛函數和缺省實參

    虛函數可以有缺省實參,但是和一般的函數不同,基類和派生類可以分別有自己的缺省實參。

    對象:

    如果存在缺省實參,那么在編譯期就確定這個虛函數調用的缺省實參的值。如果調用call忽略的實參有缺省值,那么這個缺省值由虛函數調用的對象的類型決定。

    引用和指針:

    當虛函數通過基類的引用或者指針來調用時,缺省的實參值就是基類里虛函數聲明的值。

    當虛函數通過派生類的引用或者指針來調用時,缺省的實參值就是派生類里虛函數聲明的值。

    Trouble Shooting:

    如果把基類的引用或者指針指向派生類對象,那么通過這個引用或者指針調用虛函數時,就會把基類虛函數的缺省實參值就會傳遞給派生類的虛函數。L

    15.2.5公用、私有和受保護的繼承(Public, Private, and Protected Inheritance

    這,說的是繼承方式。繼承方式不同對成員的訪問屬性也是有影響的。

    1. 公用繼承public inheritance
    2. 受保護繼承protected inheritance
    3. 私有繼承private inheritance

    訪問屬性是針對派生類的用戶來說的,而對于派生類來說,不管何種繼承方式也不會影響其訪問基類的成員。

    但是這種說法不具有傳遞性,就是說派生類的派生類來說,這些繼承方式,還是會起作用的。下面的例子就是這樣:

    class Base {

     public:

         void basemem();   // public member

     protected:

         int i;            // protected member

         // ...

     };

     struct Public_derived : public Base {

         int use_base() { return i; } // ok: derived classes can access i

         // ...

     };

     struct Private_derived : private Base {

         int use_base() { return i; } // ok: derived classes can access i

     };

    struct Derived_from Private : public Private_derived {

         // error: Base::i is private in Private_derived

         int use_base() { return i; }

     };

     struct Derived_from_Public : public Public_derived {

         // ok: Base::i remains protected in Public_derived

         int use_base() { return i; }

     };

    Derived_from對于i就不能再訪問了。因為Private_derivedprivate繼承,導致iPrivate_derived的訪問屬性就是private

    接口繼承和實現繼承

    接口繼承Interface Inheritancepublic繼承方式;派生類和基類有同樣的接口。

    實現繼承Implementation Inheritanceprotected或者private繼承方式。

    免除個別的成員Exempting Individual Members

    派生類能夠恢復繼承的成員的訪問級別,但是改變基類定義的訪問級別,增加或者減少都是不可以的。(The derived class can restore the access level of an inherited member. The access level cannot be made more or less restrictive than the level originally specified within the base class.

    應用場景:以private方式繼承時,又希望不改變其中某些成員的訪問級別。

    解決方法:使用using聲明。例如:基類定義

    class Base {

    public:

        std::size_t size() const { return n; }

    protected:

        std::size_t n;

    };

    派生類定義:

    class Derived : private Base {

    public:

       // maintain access levels for members related to the size of the object

       using Base::size;

    protected:

        using Base::n;

        // ...

     };

    這樣,派生類雖然是以private方式繼承的基類,成員sizen還是可以保持原來的訪問級別。

    缺省繼承保護級別Default Inheritance Protection Levels

    類的定義可以使用兩種關鍵字:classstruct。二者的區別在于缺省情況下,classprivate方式來繼承;而struct是以public方式來繼承。

    class Base { /* ... */ };

     struct D1 : Base { /* ... */ };   // public inheritance by default

     class D2 : Base { /* ... */ };    // private       inheritance by default

    15.2.6友元和繼承

    首先友元是不繼承的。(Friendship is not inherited.

    這句話要從兩方面來理解:

    1.    基類的友元不是派生類的友元。

    2.    友元的派生類不是友元。

    具體的sampleP575的例子。

    15.2.7 繼承和靜態成員

    對于每個靜態成員只存在一個實例。(exists a single instance of each static member.

    struct Base {

        static void statmem(); // public by default

    };

    struct Derived : Base {

        void f(const Derived&);

    };

    void Derived::f(const Derived &derived_obj)

    {

       Base::statmem();      // ok: Base defines statmem

       Derived::statmem();   // ok: Derived in herits statmem

       // ok: derived objects can be used to access static from base

       derived_obj.statmem();     // accessed through Derived object

       statmem();                 // accessed through this class

    15.3轉換和繼承Conversions and Inheritance   

    15.3.1. 從派生類到基類的轉換Derived-to-Base Conversions

    轉換成引用和轉換成對象是不一樣的(這指的是函數形參)

    1.      當把一個對象傳遞給一個需要引用的函數時,對象并沒有copy,引用直接綁定到對象上。

    2.      當把一個對象傳遞給一個需要對象的函數時,派生對象的基類部分copy到形參里。

    使用派生類的對象賦值或者初始化一個基類對象

    基類是不大可能顯式定義如何使用派生類去初始化或者賦值一個基類的對象。正如前面所闡述的,如果形參是引用,那么就可以直接把一個基類的引用綁定到派生類的對象上。因此,激烈一般都會提供形參是引用的拷貝構造函數,或者是賦值操作符。

    從派生類到基類轉換的訪問性

    這取決于派生類指定的繼承方式。(Whether the conversion is accessible depends on the access label specified on the derived class' derivation.)如果基類的public成員是可訪問的,那么轉換是可訪問的,否則,則是不可訪問的。(whether a public member of the base class would be accessible. If so, the conversion is accessible; otherwise, it is not.

    15.4 Constructors and Copy Control                     

    當構造、拷貝、賦值或撤銷一個派生類對象時,也要構造、拷貝、賦值或撤銷那些基類的子對象。(When we construct, copy, assign, or destroy an object of derived type, we also construct, copy, assign, or destroy those base-class subobjects.

    15.4.1 基類構造函數和拷貝控制

    繼承對于基類的唯一的影響就是如果希望構造函數只能北派生類訪問,那么構造函數就要定義為protected

    15.4.2 派生類的構造函數

    派生類的構造函數除了要完成自己的數據成員的初始化還要初始化它的基類。(Each derived constructor initializes its base class in addition to initializing its own data members.

    另外,只有直接基類才能夠被初始化。(Only an Immediate Base Class May Be Initialized

    派生類的初始化順序是先調用基類的初始化函數,再初始化自己的數據成員。

    只有缺省的構造函數隱式調用基類的缺省構造函數。

    class Bulk_item : public Item_base {

    public:

        Bulk_item(): min_qty(0), discount(0.0) { }

        // as before

    };

    其它構造函數要顯式定義指定的構造函數:

    class Bulk_item : public Item_base {

     public:

         Bulk_item(const std::string& book, double sales_price,

                   std::size_t qty = 0, double disc_rate = 0.0):

                      Item_base(book, sales_price),

                      min_qty(qty), discount(disc_rate) { }

         // as before

     };

    15.4.3 拷貝控制和繼承

    拷貝控制和賦值操作符都是先執行基類部分的拷貝控制和賦值,然后再執行派生類的拷貝控制和賦值。這個順序是不能該改變的。而析構函數則相反。派生類的析構函數是先調用,然后再調用基類的析構函數。

    定義派生類的拷貝構造函數:

    class Base { /* ... */ };

    class Derived: public Base {

    public:

        // Base::Base(const Base&) not invoked automatically

        Derived(const Derived& d):

             Base(d) /* other member initialization */ { /*... */ }

     };

    定義派生類的賦值操作符:

    // Base::operator=(const Base&) not invoked automatically

    Derived &Derived::operator=(const Derived &rhs)

    {

       if (this != &rhs) {

           Base::operator=(rhs); // assigns the base part

           // do whatever needed to clean up the old value in the derived part

           // assign the members from the derived

       }

       return *this;

         }

    定義派生類的析構函數:

    class Derived: public Base {

    public:

        // Base::~Base invoked automatically

        ~Derived()    { /* do what it takes to clean up derived members */ }

     };

    這三種函數無論如何定義,都要顯式調用基類對應的函數。

    15.4.4 Virtual Destructors

    先要明白為啥要把析構函數定義為virtual

    因為如果基類指針指向的是一個派生類的對象,就會導致基類的析構函數被調用,然后清除基類的成員。但是派生類的成員呢?沒人管了唄L

    因此提出的解決方法就是析構函數定義成虛函數,這樣,通過指針調用時,調用哪一個析構函數取決于指向的對象的類型。(If the destructor is virtual, then when it is invoked through a pointer, which destructor is run will vary depending on the type of the object to which the pointer points

    繼承層次關系的根類應該定義虛函數性質的析構函數,即使這個析構函數不做任何的事情。(The root class of an inheritance hierarchy should define a virtual destructor even if the destructor has no work to do.

    拷貝構造函數和賦值操作符不必是虛函數

    這很好理解,因為無論是拷貝構造函數還是賦值操作符,需要的參數類型都是類本身。

    15.4.5. 構造函數和析構函數中的調用虛函數

    這個問題的提出源自當執行構造函數或者是析構函數時,對象是不完整的。編譯器這時認為對象類型是變化的。在調用基類的構造函數或者析構函數時,派生類的對象被看作是基類類型。

    因此這就引出了問題,如果這時調用虛函數,那么是調用基類的?還是調用派生類的?

    結論:如果構造函數或者析構函數調用虛函數,虛函數的版本取決于構造函數或者析構函數的類型。(If a virtual is called from inside a constructor or destructor, then the version that is run is the one defined for the type of the constructor or destructor itself.

    這句話的意思就是說,情況一,在派生類的構造函數里調用基類的構造函數,此時如果基類的構造函數調用一個虛函數,那么這個虛函數一定就是基類中的那個版本。情況二,在派生類的構造函數里調用基類的構造函數,然后再調用某個虛函數,那么這個虛函數一定就是派生類里的那個版本。

    15.5 繼承下的類作用域Class Scope under Inheritance    

    (這部分最好先回去溫習一下15.2.4virtual和其他成員函數)

    以下這些知識點重新抄寫一遍在這里:

    為什么要定義為虛函數,不是虛的,有啥區別?

    定義為虛函數,才能實現動態綁定。在運行期確定虛函數的調用。非虛函數的調用是在編譯期確定的。

    如果函數沒有定義為虛函數,即使派生類中override這個函數,當通過指向派生類的基類指針調用這個函數時,也是調用的基類的函數。在編譯期就確定了。

    如果這個函數定義為虛函數,即使通過指向派生類的基類指針調用這個函數時,也是調用的派生類的函數。在運行期就確定了。

    靜態類型static type:在編譯期就可以確定引用和指針的類型

    動態類型dynamic type:在運行期才能確定引用和指針綁定的對象類型。

    基本概念就是先在派生類中尋址,如果未找到,則在基類中尋址。(If a name is unresolved within the scope of the derived class, the enclosing base-class scope(s) are searched for a definition of that name.

    15.5.1 名字尋址發生在編譯期

    對象、引用或指針的靜態類型決定了對象能夠完成的行為。(The static type of an object, reference, or pointer determines the actions that the object can perform.

    15.5.2 名字沖突和繼承

    當派生類的成員和基類成員名字相同時,派生類的成員屏蔽了對基類此成員的直接訪問。(A derived-class member with the same name as a member of the base class hides direct access to the base-class member.)如果還要訪問這個隱藏的成員,就需要通過作用域操作符來訪問。

    由于在基類Base和派生類Derived中都定義了成員mem,這樣基類的成員mem就會被派生類隱藏。如果還需要訪問基類的這個mem,就需要使用作用域操作符。

    struct Derived : Base {

         int get_base_mem() { return Base::mem; }

     };

    15.5.3 作用域和成員函數

    和上面的數據成員的處理方式一樣。派生類成員隱藏基類成員。這里只要名稱相同,即使原型不同也都會被隱藏。下面這個例子就是很好的說明。Derived里面定義的int memfcn(int);雖然形參和基類不同,也會導致基類的int memfcn();被屏蔽。

    struct Base {

         int memfcn();

     };

     struct Derived : Base {

         int memfcn(int); // hides memfcn in the base

     };

     Derived d; Base b;

     b.memfcn();        // calls Base::memfcn

     d.memfcn(10);      // calls Derived::memfcn

     d.memfcn();        // error: memfcn with no arguments is hidden

     d.Base::memfcn(); // ok: calls Base::memfcn

    d.memfcn();為什么不對?因為編譯器一旦在Derived類中找到memfcn就不再找其它的類了,而Derived類中定義的其實是int memfcn(int);,因此就翹了。

    結論:

    當調用派生對象的函數時,實參必須和派生類中函數定義相互匹配。基類的韓式只有在派生類中沒有任何定義時才會被調用。(When the function is called through a derived object, the arguments must match a version of the function defined in the derived class. The base class functions are considered only if the derived does not define the function at all.

    Overloaded Functions

    如果派生類重新定義了任何一個重載成員(overloaded members),那么只有在派生類中重新定義的成員可以通過派生類類型來訪問。(If the derived class redefines any of the overloaded members, then only the one(s) redefined in the derived class are accessible through the derived type.

    這意味,如果派生類希望可以訪問所有重載成員(overloaded members),要么就重新定義所有的,要么就一個都不定義。

    但是大多數情況,我們都是希望重載某些函數,繼承其他的。按照上面的說法,就需要在派生類中對于繼承基類版本的每一個函數都要重新定義。這,這,這,這工作量。所以解決方法就是使用using聲明。

    15.5.4 虛函數和作用域

    下面大師給的例子有助于理解為什么虛函數在基類和派生類里必須有相同的原型。

    答案就是:

    如果基類成員與派生類成員接受的實參不同,就沒有辦法通過基類類型的引用或指針調用派生類函數。(If the base member took different arguments than the derived-class member, there would be no way to call the derived function from a reference or pointer to the base type.

    class Base {

     public:

         virtual int fcn();

     };

     class D1 : public Base {

     public:

          // hides fcn in the base; this fcn is not virtual

          int fcn(int); // parameter list differs from fcn in Base

          // D1 inherits definition of Base::fcn()

     };

     class D2 : public D1 {

     public:

         int fcn(int); // nonvirtual function hides D1::fcn(int)

         int fcn();    // redefines virtual fcn from Base

     };

    //calling…

    Base bobj; D1 d1obj; D2 d2obj;

    Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;

    bp1->fcn();   // ok: virtual call, will call Base::fcnat run time

    bp2->fcn();   // ok: virtual call, will call Base::fcnat run time

    bp3->fcn();   // ok: virtual call, will call D2::fcnat run time

    //my calling J

    D1 *d1bp = &d1obj;

    D1bp->fcn(); //error.

    bp2->fcn();   之所以OK,因為是通過基類指針調用的;如果通過派生類的指針調用D1bp->fcn(); ,就會產生Error。道理就是由于D1里定義了int fcn(int);就導致基類的fcn()被屏蔽了。 

    名字尋址和繼承    

    遵循的是以下的4步:

    1. 開始于確定函數調用的對象,引用或指針的靜態類型(Start by determining the static type of the object, reference, or pointer through which the function is called.                              
    2. 在類中搜尋函數。如果沒找到,就在直接基類中找,依此類推,沿著類的繼承鏈向上尋找,直至函數找到或者最后一個也被搜尋過為止。如果在類或者類的基類中都沒有找到這個函數,這次調用就是錯誤的。(Look for the function in that class. If it is not found, look in the immediate base class and continue up the chain of classes until either the function is found or the last class is searched. If the name is not found in the class or its enclosing base classes, then the call is in error.
    3. 一旦找到名字,就進行類型檢測,確定和找到的函數定義相比,這個調用是否合法。(Once the name is found, do normal type-checking to see if this call is legal given the definition that was found.
    4. 假設調用是合法的,編譯器生成代碼。如果函數是虛函數,并且調用是通過引用或者指針,編譯器基于動態類型生成代碼確定調用哪個版本。如果不是虛函數,編譯器直接生成代碼調用函數。(Assuming the call is legal, the compiler generates code. If the function is virtual and the call is through a reference or pointer, then the compiler generates code to determine which version to run based on the dynamic type of the object. Otherwise, the compiler generates code to call the function directly.

    15.6 純虛函數Pure Virtual Functions  

     純虛函數引出的是抽象基類。定義:

    class Disc_item : public Item_base {

     public:

         double net_price(std::size_t) const = 0;

     };

    類包含或者繼承一個或多個純虛函數,這樣的類就是抽象基類。除了作為抽象類的派生類對象的一部分,是不能夠生成抽象基類對象的。(A class containing (or inheriting) one or more pure virtual functions is an abstract base class. We may not create objects of an abstract type except as parts of objects of classes derived from the abstract base.

    C++里面的這個純虛函數其實就相當于Java里面的抽象函數。在Java里面,如果定義了抽象函數,那么類也就是抽象類,也是不能創建這種類的對象的。

    15.7 容器和繼承Containers and Inheritance        

    這個問題的引用場景很好理解,如果要建立一個購物車來保存客戶購買的書。這個購物車可以看作是一個容器類型。這時問題就出現了,客戶購買的書可能會根據不同的折扣方式計價,而不同的計價方式就對應了不同Item_base的派生類。那么這個容器的單元類型該如何定義了,定義成基類,就會丟失折扣計價方式;定義派生類,那基類的對象也不行。

    如何解決這個問題呢?

    那就是使用容器來保存對象指針,而不是保存對象本身。(The only viable alternative would be to use the container to hold pointers to our objects.

    15.8 句柄類和繼承Handle Classes and Inheritance 

    開篇就是一句耐人尋味的話:C++不能用對象支持面向對象編程,必須使用指針或引用來支持面向對象編程。(One of the ironies of object-oriented programming in C++ is that we cannot use objects to support it. Instead, we must use pointers and references, not objects.

    句柄類保存和管理基類指針。(The handle class stores and manages a pointer to the base class.)用戶是通過句柄來訪問繼承層級中的操作。(Users access the operations of the inheritance hierarchy through the handle.

    自問自答:一個指針是不是就對應一個句柄呢?

    是的,一個指針指向一個對象,就是一個句柄,這個句柄才能保存到容器里。

    15.8.1 指針型句柄(A Pointerlike Handle)

    指針型句柄的數據成員包括兩個指針:一個是指向基類的指針;另一個是用戶計數。

    指針型句柄指向的對象是和句柄綁定在一起的。這句話的理解就是句柄創建自己的對象,句柄中的指針是指向或則個由句柄創建的對象。

    15.8.2. 復制未知類型(Cloning an Unknown Type)

    保存到容器的單元類型必須實現虛函數clone

    自問自答

    1.      派生類如何屏蔽的基類的函數(非虛函數)

    Answer:在派生類中定義和基類同名的函數(但是形參可以不同于基類)。

    2.      在繼承關系中,重載函數的調用規則

    Answer:動態綁定時,通過基類指針(指向的是派生類對象)訪問虛函數,編譯器在運行期就會確定調用派生類中的函數版本;如果派生類中沒有重新定義這個虛函數,就調用基類中這個函數的版本。

    花了大半天的時間把TextQuery做了,當然有一部分還是參考了下載的原書的source code
    TextQuery 下載

    posted on 2009-07-15 16:36 amenglai 閱讀(1102) 評論(0)  編輯  收藏 所屬分類: C++ Primer 之 讀書筆記

    主站蜘蛛池模板: 亚洲国产综合AV在线观看| 亚洲依依成人精品| 国产精品怡红院永久免费| av免费不卡国产观看| 亚洲宅男永久在线| 18观看免费永久视频| 亚洲高清美女一区二区三区| 三年片在线观看免费观看大全一| 亚洲VA中文字幕不卡无码| 国产自国产自愉自愉免费24区| 亚洲日韩精品射精日| 亚洲制服丝袜一区二区三区| 免费在线视频你懂的| 亚洲人成网国产最新在线| 永久免费AV无码国产网站| 亚洲gay片在线gv网站| 日韩精品电影一区亚洲| 中文字幕永久免费| 久久久久久亚洲精品中文字幕| 88xx成人永久免费观看| 亚洲va在线va天堂成人| 免费在线看片网站| 成全视频高清免费观看电视剧| 亚洲人成在线电影| 成人免费无码大片a毛片软件| 亚洲A∨精品一区二区三区下载| 成人亚洲综合天堂| 国产一级片免费看| 亚洲永久在线观看| 亚洲国产一区二区三区| 久久亚洲免费视频| 亚洲日韩AV一区二区三区四区 | 人成电影网在线观看免费| 国产亚洲精久久久久久无码| 美丽的姑娘免费观看在线播放 | 成人片黄网站A毛片免费| 国产亚洲综合精品一区二区三区| 久久99精品免费视频| 亚洲国产乱码最新视频| 国产亚洲一区二区三区在线不卡| 久久A级毛片免费观看|