3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第12章-cpp类和动态内存分配

發布時間:2024/1/18 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第12章-cpp类和动态内存分配 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本章內容包括:
? 對類成員使用動態內存分配。
? 隱式和顯式復制構造函數。
? 隱式和顯式重載賦值運算符。
? 在構造函數中使用new所必須完成的工作。
? 使用靜態類成員。
? 將定位new運算符用于對象。
? 使用指向對象的指針。
? 實現隊列抽象數據類型(ADT)。

通常,最好是在程序運行時(而不是編譯時)確定諸如使用多少內存等問題。對于在對象中保存姓名來說,通常的C++方法是,在類構造函數中使用new運算符在程序運行時分配所需的內存。為此,通常的方法是使用string類,它將為您處理內存管理細節。但這樣您就沒有機會更深入地學習內存管理了,因此這里將直接對問題發起攻擊。除非同時執行一系列額外步驟,如擴展類析構函數、使所有的構造函數與new析構函數協調一致、編寫額外的類方法來幫助正確完成初始化和賦值(當然,本章將介紹這些步驟),否則,在類構造函數中使用new將導致新問題。?

動態內存和類

您希望下個月的早餐、午餐和晚餐吃些什么?在第三天的晚餐喝多少盎司的牛奶?在第15天的早餐中需要在谷類食品添加多少葡萄干?如果您與大多數人一樣,就會等到進餐時再做決定。C++在分配內存時采取的部分策略與此相同,讓程序在運行時決定內存分配,而不是在編譯時決定。這樣,可根據程序的需要,而不是根據一系列嚴格的存儲類型規則來使用內存。C++使用new和delete運算符來動態控制內存。遺憾的是,在類中使用這些運算符將導致許多新的編程問題。在這種情況下,析構函數將是必不可少的,而不再是可有可無的。有時候,還必須重載賦值運算符,以保證程序正常運行。下面來看一看這些問題。

?復習示例和靜態成員

// strngbad.h -- flawed string class definition #include <iostream> #ifndef STRNGBAD_H_ #define STRNGBAD_H_ class StringBad { private:char* str; // pointer to stringint len; // length of stringstatic int num_strings; // number of objects public:StringBad(const char* s); // constructorStringBad(); // default constructor~StringBad(); // destructorfriend std::ostream& operator<<(std::ostream& os, const StringBad& st); // friend function }; #endif

首先,它使用char指針(而不是char數組)來表示姓名。這意味著類聲明沒有為字符串本身分配存儲空間,而是在構造函數中使用new來為字符串分配空間。這避免了在類聲明中預先定義字符串的長度。

其次,將num_strings成員聲明為靜態存儲類。靜態類成員有一個特點:無論創建了多少對象,程序都只創建一個靜態類變量副本。也就是說,類的所有對象共享同一個靜態成員,就像家中的電話可供全體家庭成員共享一樣。假設創建了10個StringBad對象,將有10個str成員和10個len成員,但只有一個共享的num_strings成員(參見下圖)。這對于所有類對象都具有相同值的類私有數據是非常方便的。例如,num_strings成員可以記錄所創建的對象數目。

// strngbad.cpp -- StringBad class methods #include <cstring> // string.h for some #include "strngbad.h" using std::cout; // initializing static class member int StringBad::num_strings = 0; // construct StringBad from C string StringBad::StringBad(const char* s) {len = std::strlen(s);str = new char[len + 1]; // allot storagestd::strcpy(str, s); // 不能 str = s,程序解讀里有解釋!!!num_strings++;cout << num_strings << ": \"" << str<< "\" object created\n"; } StringBad::StringBad() { // default constructorlen = 4;str = new char[4];std::strcpy(str, "C++"); // default stringnum_strings++;cout << num_strings << ": \"" << str<< "\" default object created\n"; // FYI } StringBad::~StringBad() { // 必要的析構器!!!cout << "\"" << str << "\" object deleted, "; // FYI--num_strings; // 必要!cout << num_strings << " left\n"; // FYIdelete[] str; // 必要! } std::ostream& operator<<(std::ostream& os, const StringBad& st) {os << st.str;return os; }

首先,請注意程序清單中的下面一條語句:

int StringBad::num_strings = 0;

請注意,不能在類聲明中初始化靜態成員變量,這是因為聲明描述了如何分配內存,但并不分配內存但可以在類聲明中初始化靜態成員【常量】。您可以使用這種格式來創建對象,從而分配和初始化內存。對于靜態類成員,可以在類聲明之外使用單獨的語句來進行初始化,這是因為靜態類成員是單獨存儲的,而不是對象的組成部分。請注意,初始化語句指出了類型,并使用了作用域運算符,不必再加上static關鍵字。它是private的,int?StringBad::num_strings也可這樣訪問?當然可以,這只是在實現代碼中完成的,又不是在外部通過作用域運算符直接訪問。

// strngBad.h class StringBad { private:static int num_strings; } ———————————————————————————————— // fileX.cpp int StringBad::num_strings = 0; // OK! int main() {int StringBad::num_strings = 0; // ERROR! C++ member cannot be defined in the current scopereturn 0; }

初始化是在方法文件中,而不是在類聲明文件中進行的,這是因為類聲明位于頭文件中,程序可能將頭文件包含在其他幾個文件中。如果在頭文件中進行初始化,將出現多個初始化語句副本,從而引發錯誤。

(C++11允許這樣做)

根據錯誤提示,加上const關鍵字:static const int num_strings = 0;?就不會報錯了,但后面的cpp代碼就不能修改此值了。

但int len=0不會報錯,與提示自相矛盾。這是為何?事實上在VS2019上運行,對于普通成員變量是可以賦初值的,比如賦值為int len=99,然后有個方法getLen()返回這個值 ,如果中間這個值沒有被初始化改變,那么在其他file.cpp中調用getLen()返回的就是99。這點與JAVA是相同的,即默認值。不過一般不這么做,C++中賦初值是在另一個include<xxx.h>的file中進行賦值的。

有兩種情況可以在類聲明中初始化靜態數據成員?靜態數據成員為整型const;?枚舉型const。

靜態數據成員在類聲明中聲明,在包含類方法的文件中初始化。初始化時使用作用域運算符來指出靜態成員所屬的類。但如果靜態成員是整型const或枚舉型const,則可以在類聲明中初始化。

構造函數使用strcpy()將傳遞的字符串復制到新的內存中,并更新對象計數。要理解這種方法,必須知道字符串并不保存在對象中。字符串單獨保存在堆內存中,對象僅保存了指出到哪里去查找字符串的信息。不能這樣做:str = s;因為這只保存了地址,而沒有創建字符串副本。

該析構函數首先指出自己何時被調用。這部分包含了豐富的信息,但并不是必不可少的。然而,delete語句卻是至關重要的。str成員指向new分配的內存。當StringBad對象過期時,str指針也將過期。但str指向的內存仍被分配,除非使用delete將其釋放。刪除對象可以釋放對象本身占用的內存,但并不能自動釋放屬于對象成員的指針指向的內存。因此,必須使用析構函數。在析構函數中使用delete語句可確保對象過期時,由構造函數使用new分配的內存被釋放。

下面的程序將對象聲明放在一個內部代碼塊中,因為析構函數將在定義對象的代碼塊執行完畢時調用。如果不這樣做,析構函數將在main()函數執行完畢時調用,導致您無法在執行窗口關閉前看到析構函數顯示的消息。

// vegnews.cpp -- using new and delete with classes // compile with strngbad.cpp #include <iostream> using std::cout; #include "strngbad.h" void callme1(StringBad&); // pass by reference void callme2(StringBad); // pass by value int main() {using std::endl;{cout << "Starting an inner block.\n";StringBad headline1("Celery Stalks at Midnight");StringBad headline2("Lettuce Prey");StringBad sports("Spinach Leaves Bowl for Dollars");cout << "headline1: " << headline1 << endl;cout << "headline2: " << headline2 << endl;cout << "sports: " << sports << endl;callme1(headline1);cout << "headline1: " << headline1 << endl;callme2(headline2);cout << "headline2: " << headline2 << endl;cout << "Initialize one object to another:\n";StringBad sailor = sports;cout << "sailor: " << sailor << endl;cout << "Assign one object to another:\n";StringBad knot;knot = headline1;cout << "knot: " << knot << endl;cout << "Exiting the block.\n";}cout << "End of main()\n";return 0; } void callme1(StringBad& rsb) {cout << "String passed by reference:\n";cout << " \"" << rsb << "\"\n"; } void callme2(StringBad sb) {cout << "String passed by value:\n";cout << " \"" << sb << "\"\n"; }

在Dev C++和Code Blocks上運行不了,在VS2019上有時無法運行,有時運行了,但會有異常,并且程序無響應。

Starting an inner block. 1: "Celery Stalks at Midnight" object created 2: "Lettuce Prey" object created 3: "Spinach Leaves Bowl for Dollars" object created headline1: Celery Stalks at Midnight headline2: Lettuce Prey sports: Spinach Leaves Bowl for Dollars String passed by reference:"Celery Stalks at Midnight" headline1: Celery Stalks at Midnight String passed by value:"Lettuce Prey" "Lettuce Prey" object deleted, 2 left headline2: 葺葺葺葺葺葺葺葺軸Initialize one object to another: sailor: Spinach Leaves Bowl for Dollars Assign one object to another: 3: "C++" default object created knot: Celery Stalks at Midnight Exiting the block. "Celery Stalks at Midnight" object deleted, 2 left "Spinach Leaves Bowl for Dollars" object deleted, 1 left "葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺P" object deleted, 0 left "@g" object deleted, -1 left "-|" object deleted, -2 left End of main()

程序開始時還是正常的,但逐漸變得異常,最終導致了災難性結果。

首先來看正常的部分。構造函數指出自己創建了3個StringBad對象,并為這些對象進行了編號,然后程序使用重載運算符>>列出了這些對象:

1: "Celery Stalks at Midnight" object created 2: "Lettuce Prey" object created 3: "Spinach Leaves Bowl for Dollars" object created headline1: Celery Stalks at Midnight headline2: Lettuce Prey sports: Spinach Leaves Bowl for Dollars

然后,程序將headline1傳遞給callme1()函數,并在調用后重新顯示headline1。代碼如下:

callme1(headline1); cout << "headline1: " << headline1 << endl;

下面是運行結果:

String passed by reference:"Celery Stalks at Midnight" headline1: Celery Stalks at Midnight

隨后程序執行了如下代碼:

callme2(headline2); cout << "headline2: " << headline2 << endl;

這里,callme2()按值(而不是按引用)傳遞headline2,結果表明這是一個嚴重的問題!

String passed by value:"Lettuce Prey" "Lettuce Prey" object deleted, 2 left headline2: 葺葺葺葺葺葺葺葺軸

首先,將headline2作為函數參數來傳遞從而導致析構函數被調用。其次,雖然按值傳遞可以防止原始參數被修改,但實際上函數已使原始字符串無法識別,導致顯示一些非標準字符(顯示的文本取決于內存中包含的內容)。

但headline2按值傳遞,就是復制了一份拷貝,暫且叫headline2_copy吧,那原headline2不是沒動嗎?所以這個“復制”到底是復制了些對象的哪些內容呢?請看下文關于“復制構造函數”。StringBad knot;knot = headline1;這兩句,請看下文關于“賦值運算符”。

請看輸出結果,在為每一個創建的對象自動調用析構函數時,情況更糟糕:

Exiting the block. "Celery Stalks at Midnight" object deleted, 2 left "Spinach Leaves Bowl for Dollars" object deleted, 1 left "葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺P" object deleted, 0 left "@g" object deleted, -1 left "-|" object deleted, -2 left End of main()

因為自動存儲對象被刪除的順序與創建順序相反(棧:先進后),所以最先刪除的3個對象是knots、sailor和sport。刪除knots和sailor時是正常的,但在刪除sport時,Dollars變成了“葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺P”。對于sport,程序只使用它來初始化sailor,但這種操作修改了sport。最后被刪除的兩個對象(headline2和headline1)已經無法識別。這些字符串在被刪除之前,有些操作將它們搞亂了。另外,計數也很奇怪,如何會余下?2個對象呢?

實際上,計數異常是一條線索。因為每個對象被構造和析構一次,因此調用構造函數的次數應當與析構函數的調用次數相同。對象計數(num_strings)遞減的次數比遞增次數多2,這表明使用了不將num_string遞增的構造函數創建了兩個對象。類定義聲明并定義了兩個構造函數(這兩個構造函數都使num_string遞增),但結果表明程序使用了3個構造函數。例如,請看下面的代碼:

StringBad sailor = sports;

這使用的是哪個構造函數呢?不是默認構造函數,也不是參數為const char *的構造函數。記住,以上形式的初始化等效于下面的語句

StringBad sailor = StringBad(sports);

因為sports的類型為StringBad,因此相應的構造函數原型應該如下:

StringBad(const StringBad &);

當您使用一個對象來初始化另一個對象時,編譯器將自動生成上述構造函數(稱為復制構造函數,因為它創建對象的一個副本)。自動生成的構造函數不知道需要更新靜態變量num_string,因此會將計數方案搞亂。

下圖可以更直觀了解代碼與打印情況:

實際上,這個例子說明的所有問題都是由編譯器自動生成的成員函數引起的,下面介紹這個主題。?

?特殊成員函數

StringBad類的問題是由特殊成員函數引起的。這些成員函數是自動定義的,就StringBad而言,這些函數的行為與類設計不符。具體地說,C++自動提供了下面這些成員函數:

  • 默認構造函數,如果沒有定義構造函數
  • 默認析構函數,如果沒有定義
  • 復制構造函數,如果沒有定義
  • 賦值運算符,如果沒有定義
  • 地址運算符,如果沒有定義

更準確地說,編譯器將生成上述最后三個函數的定義——如果程序使用對象的方式要求這樣做。例如,如果您將一個對象賦給另一個對象,編譯器將提供賦值運算符的定義。

結果表明,StringBad類中的問題是由隱式復制構造函數和隱式賦值運算符引起的。

隱式地址運算符返回調用對象的地址(即this指針的值)。這與我們的初衷是一致的,在此不詳細討論該成員函數。默認析構函數不執行任何操作,因此這里也不討論,但需要指出的是,這個類已經提供默認構造函數。至于其他成員函數還需要進一步討論。

C++11提供了另外兩個特殊成員函數:移動構造函數(move constructor)和移動賦值運算符(move assignment operator),這將在第18章討論。

①默認構造函數

如果沒有提供任何構造函數,C++將創建默認構造函數。例如,假如定義了一個Klunk類,但沒有提供任何構造函數,則編譯器將提供下述默認構造函數:

Klunk::Klunk() {} // 隱式地默認構造函數

也就是說,編譯器將提供一個不接受任何參數,也不執行任何操作的構造函數(默認的默認構造函數),這是因為創建對象時總是會調用構造函數:

Klunk lunk; // 調用默認構造函數

默認構造函數使lunk類似于一個常規的自動變量,也就是說,它的值在初始化時是未知的。

如果定義了構造函數,C++將不會定義默認構造函數。如果希望在創建對象時不顯式地對它進行初始化,則必須顯式地定義默認構造函數。這種構造函數沒有任何參數,但可以使用它來設置特定的值:

Klunk::Klunk() { // 顯示地定義默認構造函數klunk_ct = 0;... }

帶參數的構造函數也可以是默認構造函數,只要所有參數都有默認值。例如,Klunk類可以包含下述內聯構造函數:

Klunk(int n = 0) {klunk_ct = n; }

帶有默認值參數的構造函數也可以是默認構造函數,但只能有一個默認構造函數。也就是說,不能這樣做:

Klunk() { klunk_ct = 0; } Klunk(int n = 0) { klunk_ct = n; }

這為何有二義性呢?請看下面兩個聲明:

Klunk kar(10); // 明確匹配Klunk(int n) Klunk bus; // 兩個都能匹配

復制構造函數

復制構造函數用于將一個對象復制到新創建的對象中。也就是說,它用于初始化過程中(包括按值傳遞參數),而不是常規的賦值過程中。類的復制構造函數原型通常如下:

_Class(const _Class &);

它接受一個指向類對象的常量引用作為參數。例如,StringBad類的復制構造函數的原型如下:

StringBad(const StringBad &);

對于復制構造函數,需要知道兩點:何時調用和有何功能。

何時調用復制構造函數

新建一個對象并將其初始化為同類現有對象時,復制構造函數都將被調用。這在很多情況下都可能發生,最常見的情況是將新對象顯式地初始化為現有的對象。例如,假設motto是一個StringBad對象,則下面4種聲明都將調用復制構造函數:

StringBad ditto(motto); // 調用StringBad(const StringBad &) StringBad metoo = motto; // 調用StringBad(const StringBad &) StringBad also = StringBad(motto); // 調用StringBad(const StringBad &) StringBad *pStringBad = new StringBad(motto); // 調用StringBad(const StringBad &) 如果重載了operator+(),則下面語句將調用復制構造函數: StringBad x, y, z; x + y = z; // x + y創建臨時對象再被賦值z,創建臨時對象時調用StringBad(const StringBad &)

其中中間的2種聲明可能會使用復制構造函數直接創建metoo和also,也可能使用復制構造函數生成一個臨時對象,然后將臨時對象的內容賦給metoo和also,這取決于具體的實現。最后一種聲明使用motto初始化一個匿名對象,并將新對象的地址賦給pstring指針。

每當程序生成了對象副本時,編譯器都將使用復制構造函數。具體地說,當函數按值傳遞對象(如程序清單中的callme2())或函數返回對象時,都將使用復制構造函數。記住,按值傳遞意味著創建原始變量的一個副本。編譯器生成臨時對象時,也將使用復制構造函數。例如,將3個Vector對象相加時,編譯器可能生成臨時的Vector對象來保存中間結果。何時生成臨時對象隨編譯器而異,但無論是哪種編譯器,當按值傳遞和返回對象時,都將調用復制構造函數。具體地說,程序清單中的函數調用將調用下面的復制構造函數:

callme2(headline2);

由于按值傳遞對象將調用復制構造函數,因此應該按引用傳遞對象。這樣可以節省調用構造函數的時間以及存儲新對象的空間。

注:1.按值傳遞2.返回的是對象——都將調用復制構造函數

默認的復制構造函數的功能

注:所謂對象復制,當然就是復制對象的成員變量(不包括靜態成員),成員方法是對象本來就具有的,不存在復制的情況。

默認的復制構造函數逐個復制非靜態成員(成員復制也稱為淺復制),復制的是成員的值。在程序清單中,下述語句:

StringBad sailor = sports;

與下面的代碼等效(只是由于私有成員是無法訪問的,因此這些代碼不能通過編譯):

StringBad sailor; sailor.str = sports.str; sailor.len = sports.len;

如果成員本身就是類對象,則將使用這個類的復制構造函數來復制成員對象。靜態函數(如num_strings)不受影響,因為它們屬于整個類,而不是屬于對象。下圖說明了隱式復制構造函數執行的操作。

?回到StringBad:復制構造函數的哪里出了問題

現在介紹程序清單的兩個異常之處(假設輸出為該程序清單后面列出的)。首先,程序的輸出表明,析構函數的調用次數比構造函數的調用次數多2,原因可能是程序確實使用默認的復制構造函數另外創建了兩個對象。當callme2()被調用時,復制構造函數被用來初始化callme2()的形參,還被用來將對象sailor初始化為對象sports。默認的復制構造函數不說明其行為,因此它不指出創建過程,也不增加計數器num_strings的值。但析構函數更新了計數,并且在任何對象過期時都將被調用,而不管對象是如何被創建的。這是一個問題,因為這意味著程序無法準確地記錄對象計數。解決辦法是提供一個對計數進行更新的顯式復制構造函數:

StringBad::StringBad(const String & s) {num_string ++;... }

第二個異常之處更微妙,也更危險,其癥狀之一是字符串內容出現亂碼:

headline2: 葺葺葺葺葺葺葺葺軸

原因在于隱式復制構造函數是按值進行復制的。例如,對于程序清單,隱式復制構造函數的功能相當于:

sailor.str = sport.str;

這里復制的并不是字符串,而是一個指向字符串的指針。也就是說,將sailor初始化為sports后,得到的是兩個指向同一個字符串的指針。當operator <<()函數使用指針來顯示字符串時,這并不會出現問題。但當析構函數被調用時,這將引發問題。析構函數StringBad釋放str指針指向的內存,因此釋放sailor的效果如下:

delete [] sailor.str; // 刪除ditto.str指向的字符串

sailor.str指針指向“Spinach Leaves Bowl for Dollars”,因為它被賦值為sports.str,而sports.str指向的正是上述字符串。所以delete語句將釋放字符串“Spinach Leaves Bowl for Dollars”占用的內存。

然后,釋放sports的效果如下:

delete [] sports.str; // 效果未定義

sports.str指向的內存已經被sailor的析構函數釋放,這將導致不確定的、可能有害的后果。程序清單中的程序生成受損的字符串,這通常是內存管理不善的表現。

另一個癥狀是,試圖釋放內存兩次可能導致程序異常終止。例如,Microsoft Visual C++ 2010(調試模式)顯示一個錯誤消息窗口,指出“Debug Assertion Failed!”;而在Linux中,g++ 4.4.1顯示消息“double free or corruption”并終止程序運行。其他系統可能提供不同的消息,甚至不提供任何消息,但程序中的錯誤是相同的。

定義一個顯式復制構造函數以解決問題

解決類設計中這種問題的方法是進行深度復制(deep copy)。也就是說,復制構造函數應當復制字符串并將副本的地址賦給str成員,而不僅僅是復制字符串地址。這樣每個對象都有自己的字符串,而不是引用另一個對象的字符串。調用析構函數時都將釋放不同的字符串,而不會試圖去釋放已經被釋放的字符串。可以這樣編寫StringBad的復制構造函數:

StringBad::StringBad(const StringBad & st) {num_strings++; // 處理靜態成員的更新len = st.len; // 相同長度str = new char[len + 1]; // 分配空間std::strcpy(str, st.str); // 復制字符串到新地址cout << num_strings << ":\"" << str << "\" object created\n"; // 打印信息 }

必須定義復制構造函數的原因在于,一些類成員是使用new初始化的、指向數據的指針,而不是數據本身。下圖說明了深度復制

如果類中包含了使用new初始化的指針成員,應當定義一個復制構造函數,以復制指向的數據,而不是指針,這被稱為深度復制。復制的另一種形式(成員復制或淺復制)只是復制指針值。淺復制僅淺淺地復制指針信息,而不會深入“挖掘”以復制指針引用的結構

?StringBad的其他問題:賦值運算符

并不是程序清單的所有問題都可以歸咎于默認的復制構造函數,還需要看一看默認的賦值運算符。ANSI C允許結構賦值,而C++允許類對象賦值,這是通過自動為類重載賦值運算符實現的。這種運算符的原型如下:

_Class & _Class::operator=(const _Class &);

它接受并返回一個指向類對象的引用。例如,StringBad類的賦值運算符的原型如下:

StringBad & StringBad::operator=(const StringBad &);

賦值運算符的功能以及何時使用它

將已有的對象賦給另一個對象時,將使用重載的賦值運算符:

StringBad headline1("Celery Stalks at Midnight"); ... StringBad knot; // 自動調用默認構造函數初始化knot knot = headline1; // 賦值運算符被調用

初始化對象時,并不一定會使用賦值運算符:

StringBad metoo = knot; // 使用復制構造函數,也可能使用的是賦值運算符

這里,metoo是一個新創建的對象,被初始化為knot的值,因此使用復制構造函數。然而,正如前面指出的,實現時也可能分兩步來處理這條語句:使用復制構造函數創建一個臨時對象,然后通過賦值將臨時對象的值復制到新對象中。這就是說,初始化總是會調用復制構造函數,而使用=運算符時也可能調用賦值運算符。

與復制構造函數相似,賦值運算符的隱式實現也對成員進行逐個復制。對象賦值是通過默認賦值運算符函數實現的(如果沒有重載),把對象A的數據成員的值逐位賦值給對象B,兩個對象之間的賦值,僅僅使用這些對象中數據成員,而兩個對象仍是分離的。如果成員本身就是類對象,則程序將使用為這個類定義的賦值運算符來復制該成員,但靜態數據成員不受影響。

賦值的問題出在哪里

程序清單將headline1賦給knot:

knot = headline1;

為knot調用析構函數時,將顯示下面的消息:

"Celery Stalks at Midnight" object deleted, 2 left

為Headline1調用析構函數時,顯示如下消息(有些實現方式在此之前就異常終止了):

"-|" object deleted, -2 left

出現的問題與隱式復制構造函數相同:數據受損。這也是成員復制的問題,即導致headline1.str和knot.str指向相同的地址。因此,當對knot調用析構函數時,將刪除字符串“Celery Stalks at Midnight”;當對headline1調用析構函數時,將試圖刪除前面已經刪除的字符串。正如前面指出的,試圖刪除已經刪除的數據導致的結果是不確定的,因此可能改變內存中的內容,導致程序異常終止。要指出的是,如果操作結果是不確定的,則執行的操作將隨編譯器而異,包括顯示獨立聲明(Declaration of Independence)或釋放隱藏文件占用的硬盤空間。當然,編譯器開發人員通常不會花時間添加這樣的行為。

解決賦值的問題

對于由于默認賦值運算符不合適而導致的問題,解決辦法是提供賦值運算符(進行深度復制)定義。其實現與復制構造函數相似,但也有一些差別。

  • 由于目標對象可能引用了以前分配的數據,所以函數應使用delete[]來釋放這些數據。
  • 函數應當避免將對象賦給自身;否則,給對象重新賦值前,釋放內存操作可能刪除對象的內容。
  • 函數返回一個指向調用對象的引用。

通過返回一個對象,函數可以像常規賦值操作那樣,連續進行賦值,即如果S0、S1和S2都是StringBad對象,則可以編寫這樣的代碼:

S0 = S1 = S2;

使用函數表示法時,上述代碼為:

S0.operator=(S1.operator=(S2));

因此,S1.operator=(S2)的返回值是函數S0.operator=()的參數。

因為返回值是一個指向StringBad對象的引用,因此參數類型是正確的。

下面的代碼說明了如何為StringBad類編寫賦值運算符:

StringBad & StringBad::operator=(const StringBad & st) {if (this == &st) // 對象賦值給它自己return *this; // all donedelete [] str; // free old stringlen = st.len;str = new char[len + 1]; // 為新字符串分配空間std::strcpy(str, st.str); // copy the stringreturn *this; // return reference to invoking object }

因為定義的兩個構造都有new char[],故可delete[],this指的是調用對象(invoking object)的地址,假設調用對象為invokeObj,被賦值對象為abcObj,則使用賦值運算時,若重載了,則要注意:

1. invokeObj里面有str=new char[],所以給它賦值時,如果不清除原來的動態內存分配,就內存泄露了,所以要配套使用delele[];

2. 若invokeObj與abcObj的引用相同,用了delete[]后就釋放掉了invokeObj和abcObj的str成員了。既然相同,賦值就沒必要了,直接返回調用對象的引用*this即可。之所以返回調用對象的引用是為了滿足連續調用的需要,如invokeObj=lmnObj=xyzObj。

代碼首先檢查自我復制,這是通過查看賦值運算符右邊的地址(&s)是否與接收對象(this)的地址相同來完成的。如果相同,程序將返回*this,然后結束。第10章介紹過,賦值運算符是只能由類成員函數重載的運算符之一。

如果地址不同,函數將釋放str指向的內存,這是因為稍后將把一個新字符串的地址賦給str。如果不首先使用delete運算符,則上述字符串將保留在內存中。由于程序中不再包含指向該字符串的指針,因此這些內存被浪費掉。

接下來的操作與復制構造函數相似,即為新字符串分配足夠的內存空間,然后將賦值運算符右邊的對象中的字符串復制到新的內存單元中。

上述操作完成后,程序返回*this并結束。

賦值操作并不創建新的對象,因此不需要調整靜態數據成員num_strings的值。

將前面介紹的復制構造函數和賦值運算符添加到StringBad類中后,所有的問題都解決了。

改進后的新String類

?修訂后的默認構造函數

新的默認構造函數,它與下面類似:

String::String() {len = 0;str = new char[1];str[0] = '\0'; // 默認值 }

為什么代碼為:

str = new char[1]; 而不是: str = new char;

上面兩種方式分配的內存量相同,區別在于前者與類析構函數兼容,而后者不兼容。析構函數中包含如下代碼:

delete [] str;

delete[]與使用new[]初始化的指針和空指針都兼容。因此:

str = new char[1]; str[0] = '\0'; 可修改為: str = 0; // 設置str為空指針

對于以其他方式初始化的指針,使用delete []時,結果將是不確定的:

char words[15] = "bad idea"; char * p1 = words; char * p2 = new char; char * p3; delete [] p1; // undefined, 不要這樣做! delete [] p2; // undefined, 不要這樣做! delete [] p3; // undefined, 不要這樣做!

在C++98中,字面值0有兩個含義:可以表示數字值零,也可以表示空指針,這使得閱讀程序的人和編譯器難以區分。有些程序員使用(void *) 0來標識空指針(空指針本身的內部表示可能不是零),還有些程序員使用NULL,這是一個表示空指針的C語言宏。C++11提供了更好的解決方案:引入新關鍵字nullptr,用于表示空指針。您仍可像以前一樣使用0——否則大量現有的代碼將非法,但建議您使用nullptr:

str = nullptr; // C++11 null pointer notation

?比較成員函數

bool operator<(const String &st1, const String &st2) {return (std::strcmp(str1.str, str2.str) < 0); }

假設answer是String對象,則下面的代碼:

if ("love" == answer) 將被轉換為: if (operator==("love", answer)) 然后,編譯器將使用某個構造函數將代碼轉換為: if (operator==(String("love"), answer)) 這與原型是相匹配的。

?使用中括號表示法訪問字符

表達式city[0]中,city是第一個操作數,[]是運算符,0是第二個操作數。

假設opera是一個String對象:

String opera("The Magic Flute");

則對于表達式opera[4],C++將查找名稱和特征標與此相同的方法:

String::operator[](int i)

如果找到匹配的原型,編譯器將使用下面的函數調用來替代表達式opera[4]:

opera.operator[](4)

opera對象調用該方法,數組下標4成為該函數的參數。

下面是該方法的簡單實現:

char & String::operator[](int i) {return str[i]; }

有了上述定義后,語句:

cout << opera[4]; 將被轉換為: cout << opera.operator[](4);

返回值是opera.str[4](字符'M')。由此,公有方法可以訪問私有數據。

將返回類型聲明為char &,便可以給特定元素賦值。例如,可以編寫這樣的代碼:

String means("might"); means[0] = 'r'; 第二條語句將被轉換為一個重載運算符函數調用: means.operator[0] = 'r'; 這里將r賦給方法的返回值,而函數返回的是指向means.str[0]的引用,因此上述代碼等同于下面的代碼: means.str[0] = 'r';

代碼的最后一行訪問的是私有數據,但由于operator是類的一個方法,因此能夠修改數組的內容。最終的結果是“might”被改為“right”。

假設有下面的常量對象:

const String answer("futile");// answer聲明為常量

如果有上述operator定義,則下面的代碼將出錯:

cin >> answer[1]; // 編譯出錯

原因是answer是const,而上述方法無法確保不修改數據實際上,有時該方法的工作就是修改數據,因此無法確保不修改數據)。

在重載時,C++將區分常量和非常量函數的特征標,因此可以提供另一個僅供const String對象使用的operator版本

// 專為常量字符串而設計的版本(最后面的const說明函數的成員對象即它的調用者是不允許修改的) const char & String::operator[](int i) const {return str[i]; }

有了上述定義后,就可以讀/寫常規String對象了,消除了“無法確定不修改數據”的問題;而對于const String對象,則只能讀取其數據:

String text("Once upon a time"); const String answer("futile"); cout << text[1]; // ok,使用非常量版本的operator[](); cout << answer[1]; // ok,使用常量版本的operator[](); cin >> text[1]; // ok,使用非常量版本的operator[](); cin >> answer[1]; // 編譯出錯!!!

?靜態成員函數

可以將成員函數聲明為靜態的(函數聲明必須包含關鍵字static,但如果函數定義是獨立的,則其中不能包含關鍵字static),這樣做有兩個重要的后果。

首先,不能通過對象調用靜態成員函數;實際上,靜態成員函數甚至不能使用this指針。如果靜態成員函數是在公有部分聲明的,則可以使用類名和作用域解析運算符來調用它。例如,可以給String類添加一個名為HowMany()的靜態成員函數,方法是在類聲明中添加如下原型/定義:

static int HowMany() { return num_strings; } 調用它的方式如下: int count = String::HowMany(); // 調用一個靜態成員函數

其次,由于靜態成員函數不與特定的對象相關聯,因此只能使用靜態數據成員。例如,靜態方法HowMany()可以訪問靜態成員num_string,但不能訪問str和len。這點與JAVA是一樣的。

同樣,也可以使用靜態成員函數設置類級(classwide)標記,以控制某些類接口的行為。例如,類級標記可以控制顯示類內容的方法所使用的格式。

?進一步重載賦值運算符

介紹針對String類的程序清單之前,先來考慮另一個問題。假設要將常規字符串復制到String對象中。例如,假設使用getline()讀取了一個字符串,并要將這個字符串放置到String對象中,前面定義的類方法讓您能夠這樣編寫代碼:

String name; char temp[40]; cin.getline(temp, 40); name = temp; // 使用構造函數轉換類型

但如果經常需要這樣做,這將不是一種理想的解決方案。為解釋其原因,先來回顧一下最后一條語句是怎樣工作的。

①程序使用構造函數String(const char *)來創建一個臨時String對象,其中包含temp中的字符串副本。第11章介紹過,只有一個參數的構造函數被用作轉換函數

②程序使用String & String::operator=(const String &)函數將臨時對象中的信息復制到name對象中。

③程序調用析構函數~String()刪除臨時對象。

為提高處理效率,最簡單的方法是重載賦值運算符,使之能夠直接使用常規字符串,這樣就不用創建和刪除臨時對象了下面是一種可能的實現:

String & String::operator=(const char * s) {delete [] str;len = std::strlen(s);str = new char[len + 1];std::strcpy(str, s);return *this; }

要注意:重載運算符函數是類成員函數,所以調用它的當然是類對象。這里返回*this,只是為了滿足連續調用的需要。

一般說來,必須釋放str指向的內存,并為新字符串分配足夠的內存。

// string1.h -- fixed and augmented string class definition #ifndef STRING1_H_ #define STRING1_H_ #include <iostream> using std::ostream; using std::istream; class String { private:char* str; // pointer to stringint len; // length of stringstatic int num_strings; // number of objectsstatic const int CINLIM = 80; // cin input limit public:// constructors and other methodsString(const char* s); // constructorString(); // 默認構造函數String(const String&); // 復制構造函數~String(); // destructorint length() const {return len;}// overloaded operator methods String& operator=(const String&);String& operator=(const char*);char& operator[](int i);const char& operator[](int i) const;// overloaded operator friendsfriend bool operator<(const String& st, const String& st2);friend bool operator>(const String& st1, const String& st2);friend bool operator==(const String& st, const String& st2);friend ostream& operator<<(ostream& os, const String& st);friend istream& operator>>(istream& is, String& st);// static functionstatic int HowMany(); }; #endif // string1.cpp -- String class methods #define _CRT_SECURE_NO_WARNINGS #include <cstring> // string.h for some #include "string1.h" // includes <iostream> using std::cin; using std::cout; // initializing static class member int String::num_strings = 0; // static method int String::HowMany() {return num_strings; } // class methods String::String(const char* s) { // construct String from C stringlen = std::strlen(s); // set sizestr = new char[len + 1]; // allot storagestd::strcpy(str, s); // initialize pointernum_strings++; // set object countcout << "String(const char* s): num_string++ = " << num_strings << ":" << s << "#\n"; } String::String() { // default constructorlen = 4;str = new char[1];str[0] = '\0'; // default stringnum_strings++;cout << "String(): num_string++ = " << num_strings << ":" << str << "#\n"; } String::String(const String& st) {num_strings++; // handle static member updatecout << "String(const String& st): num_string++ = " << num_strings << ":" << st.str << "#\n";len = st.len; // same lengthstr = new char[len + 1]; // allot spacestd::strcpy(str, st.str); // copy string to new location } String::~String() { // necessary destructor--num_strings; // requiredcout << "object:\"" << str << "\" deleted.\n";delete[] str; // required } // overloaded operator methods // assign a String to a String String& String::operator=(const String& st) {if (this == &st)return *this;delete[] str;len = st.len;str = new char[len + 1];std::strcpy(str, st.str);return *this; } // assign a C string to a String String& String::operator=(const char* s) {delete[] str;len = std::strlen(s);str = new char[len + 1];std::strcpy(str, s);return *this; } // read-write char access for non-const String char& String::operator[](int i) {return str[i]; } // read-only char access for const String const char& String::operator[](int i) const {return str[i]; } // overloaded operator friends bool operator<(const String& st1, const String& st2) {return (std::strcmp(st1.str, st2.str) < 0); } bool operator>(const String& st1, const String& st2) {return st2 < st1; } bool operator==(const String& st1, const String& st2) {return (std::strcmp(st1.str, st2.str) == 0); } // simple String output ostream& operator<<(ostream& os, const String& st) {os << st.str;return os; } // quick and dirty String input istream& operator>>(istream& is, String& st) {char temp[String::CINLIM];is.get(temp, String::CINLIM);if (is)st = temp;while (is && is.get() != '\n')continue;return is; } // sayings1.cpp -- using expanded String class // compile with string1.cpp #include <iostream> #include "string1.h" const int ArSize = 10; const int MaxLen = 81; int main() {using std::cout;using std::cin;using std::endl;String name;cout << "Hi, what's your name?\n>> ";cin >> name;cout << name << ", please enter up to " << ArSize<< " short sayings <empty line to quit>:\n";String sayings[ArSize]; // array of objectschar temp[MaxLen]; // temporary string storageint i;for (i = 0; i < ArSize; i++) {cout << i + 1 << ": ";cin.get(temp, MaxLen);while (cin && cin.get() != '\n')continue;if (!cin || temp[0] == '\0') // empty line?break; // i not incrementedelsesayings[i] = temp; // 重載賦值運算符}int total = i; // total # of lines readif (total > 0) {cout << "Here are your sayings:\n";for (i = 0; i < total; i++)cout << sayings[i][0] << ": " << sayings[i] << endl;int shortest = 0;int first = 0;for (i = 1; i < total; i++) {if (sayings[i].length() < sayings[shortest].length())shortest = i;if (sayings[i] < sayings[first])first = i;}cout << "Shortest saying:\n" << sayings[shortest] << endl;;cout << "First alphabetically:\n" << sayings[first] << endl;cout << "This program used " << String::HowMany()<< " String objects. Bye.\n";} elsecout << "No input! Bye.\n";return 0; }

較早的get(char *, int)版本在讀取空行后,返回的值不為false。然而,對于這些版本來說,如果讀取了一個空行,則字符串中第一個字符將是一個空字符。這個示例使用了下述代碼:

if (!cin || temp[0] == '\0') // empty line?break; // i not incremented

如果實現遵循了最新的C++標準,則if語句中的第一個條件將檢測到空行,第二個條件用于舊版本實現中檢測空行。

String(): num_string++ = 1:# Hi, what's your name? >> |Misty Gutz Misty Gutz, please enter up to 10 short sayings <empty line to quit>: String(): num_string++ = 2:# String(): num_string++ = 3:# String(): num_string++ = 4:# String(): num_string++ = 5:# String(): num_string++ = 6:# String(): num_string++ = 7:# String(): num_string++ = 8:# String(): num_string++ = 9:# String(): num_string++ = 10:# String(): num_string++ = 11:# 1: |a fool and his money are soon parted 2: |penny wise, pound foolish 3: |the love of money is the root of much evil 4: |out of sight, out of mind 5: |absence makes the heart grow fonder 6: |absinthe makes the hart grow fonder 7: |<Enter> Here are your sayings: a: a fool and his money are soon parted p: penny wise, pound foolish t: the love of money is the root of much evil o: out of sight, out of mind a: absence makes the heart grow fonder a: absinthe makes the hart grow fonder Shortest saying: penny wise, pound foolish First alphabetically: a fool and his money are soon parted This program used 11 String objects. Bye. object:"" deleted. object:"" deleted. object:"" deleted. object:"" deleted. object:"absinthe makes the hart grow fonder" deleted. object:"absence makes the heart grow fonder" deleted. object:"out of sight, out of mind" deleted. object:"the love of money is the root of much evil" deleted. object:"penny wise, pound foolish" deleted. object:"a fool and his money are soon parted" deleted. object:"Misty Gutz" deleted.

自動存儲對象被刪除的順序與創建順序相反。所以打印object:"X" deleted.是從num_strings==11的字符串開始的。

我們回看StringBad主要代碼,直接分析潛在問題:

class StringBad { private:char* str;int len;static int num_strings; public:StringBad(const char* s);StringBad();~StringBad();friend std::ostream& operator<<(std::ostream& os, const StringBad& st); } int StringBad::num_strings = 0; StringBad::StringBad(const char* s) {len = std::strlen(s);str = new char[len + 1];std::strcpy(str, s);num_strings++;cout << num_strings << ": \"" << str<< "\" object created\n"; } StringBad::StringBad() {len = 4;str = new char[4];std::strcpy(str, "C++");num_strings++;cout << num_strings << ": \"" << str<< "\" default object created\n"; } StringBad::~StringBad() {cout << "\"" << str << "\" object deleted, ";--num_strings;cout << num_strings << " left\n";delete[] str; } std::ostream& operator<<(std::ostream& os, const StringBad& st) {os << st.str;return os; } int main() {StringBad headline1("Celery Stalks at Midnight");StringBad headline2("Lettuce Prey");StringBad sports("Spinach Leaves Bowl for Dollars");callme1(headline1);cout << "headline1: " << headline1 << endl;callme2(headline2);cout << "headline2: " << headline2 << endl;cout << "Initialize one object to another:\n";StringBad sailor = sports;cout << "sailor: " << sailor << endl;cout << "Assign one object to another:\n";StringBad knot;knot = headline1;cout << "knot: " << knot << endl;cout << "Exiting the block.\n" } void callme1(StringBad& rsb) {cout << "String passed by reference:\n";cout << " \"" << rsb << "\"\n"; } void callme2(StringBad sb) {cout << "String passed by value:\n";cout << " \"" << sb << "\"\n"; }

headline1("Celery Stalks at Midnight")、headline2("Lettuce Prey")、sports("Spinach Leaves Bowl for Dollars"),通過構造函數StringBad::StringBad(const char* s),完成了3個字符串的初始化,num_strings = 3。然后,callme1(headline1),通過按引用傳遞參數,正確打印。然后,callme2(headline2),通過按值傳遞,即先拷貝一份headline2,而每當程序生成了對象副本時,編譯器都將使用復制構造函數,而程序沒有聲明復制構造函數,所以調用了默認的復制構造函數StringBad(const StringBad &),num_strings==3(should be 4),calleme2()調用結束,也就結束了這個臨時拷貝的生命周期,自動調用一次~StringBad(),num_strings==2(should be 3),由于默認是淺拷貝,副本中成員變量char * s中s是指針,拷貝過來的就是地址,~StringBad()使得指向的空間釋放,所以原headline2指向的內容就是被釋放后的內容,所以打印:

String passed by value:"Lettuce Prey" "Lettuce Prey" object deleted, 2 left headline2: 葺葺葺葺葺葺葺葺軸

接下來,StringBad sailor = sports;這與StringBad sailor; sailor = sports;這兩條語句意思是不一樣的,后者是sailor先用默認構造函數初始化,然后再有個賦值操作;前者是用一個對象來初始化sailor,而當使用一個對象來初始化另一個對象時,使用的是復制構造函數,由于沒有自定義復制構造函數,使用的是默認的,所以num_strings==2(should be 4),正常打印輸出。再接著,StringBad knot;調用默認構造函數初始化,num_strings==3(should be 5);knot=headline1;賦值操作,對象賦值是通過默認賦值運算符函數實現的,把對象headline1的數據成員的值逐位賦值給對象knot,兩個對象之間的賦值,僅僅使這些對象中數據成員,而兩個對象仍是分離的。打印如下:

3: "C++" default object created knot: Celery Stalks at Midnight Exiting the block.

結束塊,最后自動調用~StringBad()釋放對象,實際上應該調用5次。

對象生成順序為:

headline1("Celery...")、headline2("Lettuce...")、headline2-copy("Lettuce...")【函數塊作用域,調用完callme2()自動釋放】、sports("Spinach...")、sailor("Spinach...")、knot("Celery...")。

自動存儲對象被刪除的順序與創建順序相反,釋放順序為:

knot("Celery...") 、sailor("Spinach...")、sports("Spinach...")、headline2("亂碼...")、headline1("Celery...")。

由于knot、sailor分別通過headline1、sports對象生成,賦值給成員的是地址,所以先釋放前兩對象后,對應地址指向的空間就是未知了。所以最后輸出為:

"Celery Stalks at Midnight" object deleted, 2 left "Spinach Leaves Bowl for Dollars" object deleted, 1 left "葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺P" object deleted, 0 left "@g" object deleted, -1 left "-|" object deleted, -2 left End of main()

在構造函數中使用new時就注意的事項

至此,您知道使用new初始化對象的指針成員時必須特別小心。具體地說,應當這樣做。

⑴ 如果在構造函數中使用new來初始化指針成員,則應在析構函數中使用delete。

⑵ new和delete必須相互兼容。new對應于delete,new[]對應于delete[]。

⑶ 如果有多個構造函數,則必須以相同的方式使用new,要么都帶中括號,要么都不帶。因為只有一個析構函數,所有的構造函數都必須與它兼容。然而,可以在一個構造函數中使用new初始化指針,而在另一個構造函數中將指針初始化為空(0或C++11中的nullptr),這是因為delete(無論是帶中括號還是不帶中括號)可以用于空指針。

NULL、0還是nullptr:以前,空指針可以用0或NULL(在很多頭文件中,NULL是一個被定義為0的符號常量)來表示。C程序員通常使用NULL而不是0,以指出這是一個指針,就像使用‘\0’而不是0來表示空字符,以指出這是一個字符一樣。然而,C++傳統上更喜歡用簡單的0,而不是等價的NULL。但正如前面指出的,C++11提供了關鍵字nullptr,這是一種更好的選擇。

⑷?應定義一個復制構造函數,通過深度復制將一個對象初始化為另一個對象。通常,這種構造函數與下面類似。

String::String(const String & st) {num_strings++;len = st.len;str = new char[len + 1];std::strcpy(str, st.str); }

具體地說,復制構造函數應分配足夠的空間來存儲復制的數據,并復制數據,而不僅僅是數據的地址。另外,還應該更新所有受影響的靜態類成員。

⑸ 應當定義一個賦值運算符,通過深度復制將一個對象復制給另一個對象。通常,該類方法與下面類似:

String & String::operator=(const String & st) {if (this == &st)return *this;delete[] str;len = st.len;str = new char[len + 1];std::strcpy(str, st.str);return *this; }

具體地說,該方法應完成這些操作:檢查自我賦值的情況,釋放成員指針以前指向的內存,復制數據而不僅僅是數據的地址,并返回一個指向調用對象的引用。

?應該和不應該

下面的摘要包含了兩個不正確的示例(指出什么是不應當做的)以及一個良好的構造函數示例:

String::String() {str = "default string"; // opps,no new[]len = std::strlen(str); } String::String(const char * s) {len = std::strlen(s);str = new char; // opps,no []std::strcpy(str, s); // opps,no room } String::String(const String & st) {len = st.len;str = new char[len + 1]; // good,allocate spacestd::strcpy(str, st.str); // good,copy value }

第一個構造函數沒有使用new來初始化str。對默認對象調用析構函數時,析構函數使用delete來釋放str。對不是使用new初始化的指針使用delete時,結果將是不確定的,并可能是有害的。可將該構造函數修改為下面的任何一種形式:

String::String() {len = 0;str = new char[1];str[0] = '\0'; } String::String() {len = 0;str = 0; // or,with C++11,str = nullptr; } String::String() {static const char * s = "C++"; // initialized just oncelen = std::strlen(s);str = new char[len + 1];std::strcpy(str, s); }

前面第二個構造函數使用了new,但分配的內存量不正確。因此,new返回的內存塊只能保存一個字符。試圖將過長的字符串復制到該內存單元中,將導致內存問題。另外,這里使用的new不帶中括號,這與另一個構造函數的正確格式不一致。第三個構造函數是正確的。

最后,下面的析構函數無法與前面的構造函數正常地協同工作:

String::~String() {delete str; // opps, should be delete [] str }

該析構函數未能正確地使用delete。由于構造函數創建的是一個字符數組,因此析構函數應該刪除一個數組。

?包含類成員的類的逐成員復制

假設類成員的類型為String類或標準string類:

class Magazine { private:String title;string publisher; ... };

String和string都使用動態內存分配,這是否意味著需要為Magazine類編寫復制構造函數和賦值運算符?不,至少對這個類本身來說不需要。默認的逐成員復制和賦值行為有一定的智能。如果您將一個Magazine對象復制或賦值給另一個Magazine對象,逐成員復制將使用成員類型定義的復制構造函數和賦值運算符。也就是說,復制成員title時,將使用String的復制構造函數,而將成員title賦給另一個Magazine對象時,將使用String的賦值運算符,依此類推。然而,如果Magazine類因其他成員需要定義復制構造函數和賦值運算符,情況將更復雜;在這種情況下,這些函數必須顯式地調用String和string的復制構造函數和賦值運算符,這將在第13章介紹。

有關返回對象的說明

當成員函數或獨立的函數返回對象時,有幾種返回方式可供選擇。可以返回指向對象的引用、指向對象的const引用或const對象。到目前為止,介紹了前兩種方式,但沒有介紹最后一種方式,現在是復習這些方式的好時機。

?返回指向const對象的引用

使用const引用的常見原因是旨在提高效率,但對于何時可以采用這種方式存在一些限制。如果函數返回(通過調用對象的方法或將對象作為參數)傳遞給它的對象,可以通過返回引用來提高其效率。例如,假設要編寫函數Max(),它返回兩個Vector對象中較大的一個,該函數將以下面的方式被使用:

Vector force1(50, 60); Vector force2(10, 70); Vector max; max = Max(force1, force2);

下面兩種實現都是可行的:

// version 1 Vector Max(const Vector & v1, const Vector & v2) {if (v1.magval() > v2.magval()){return v1;elsereturn v2; } // version 2 const Vector & Max(const Vector & v1, const Vector & v2) {if (v1.magval() > v2.magval()){return v1;elsereturn v2; }

這里有三點需要說明。首先,返回對象將調用復制構造函數,而返回引用不會。因此,第二個版本所做的工作更少,效率更高。其次,引用指向的對象應該在調用函數執行時存在。在這個例子中,引用指向force1或force2,它們都是在調用函數中定義的,因此滿足這種條件。第三,v1和v2都被聲明為const引用,因此返回類型必須為const,這樣才匹配。

關于const引用,參看下面代碼:

#include <iostream> int main() {int a = 1;int& b = a;const int& c = a; // 常量引用a = 5; // 改變a的值std::cout << a << "," << b << "," << c << std::endl; // 輸出:5,5,5b = 4; // 改變b的值std::cout << a << "," << b << "," << c << std::endl; // 輸出:4,4,4c = 4; // 直接出錯:常量引用。表達式必須是可修改的左值。return 0; }

不能通過const引用改變變量的值。對于對象也是一樣。

?返回指向非const對象的引用

兩種常見的返回非const對象情形是,重載賦值運算符以及重載與cout一起使用的<<運算符。前者這樣做旨在提高效率,而后者必須這樣做。

operator=()的返回值用于連續賦值:

String s1("Good stuff"); String s2, s3; s3 = s2 = s1;

在上述代碼中,s2.operator=()的返回值被賦給s3。為此,返回String對象或String對象的引用都是可行的,但與Vector示例中一樣,通過使用引用,可避免該函數調用String的復制構造函數來創建一個新的String對象。在這個例子中,返回類型不是const,因為方法operator=()返回一個指向s2的引用,可以對其進行修改(比如可進行賦值操作修改)。

operator<<()的返回值用于串接輸出:

String s1("Good stuff"); cout << s1 << " is coming!";

在上述代碼中,operator<<(cout, s1)的返回值成為一個用于顯示字符串“is coming!”的對象。返回類型必須是ostream &,而不能僅僅是ostream。如果使用返回類型ostream,將要求調用ostream類的復制構造函數,而ostream沒有公有的復制構造函數。幸運的是,返回一個指向cout的引用不會帶來任何問題,因為cout已經在調用函數的作用域內。

?返回對象

如果被返回的對象是被調用函數中的局部變量,則不應按引用方式返回它,因為在被調用函數執行完畢時,局部對象將調用其析構函數。因此,當控制權回到調用函數時,引用指向的對象將不再存在。在這種情況下,應返回對象而不是引用。通常,被重載的算術運算符屬于這一類。請看下述示例,它再次使用了Vector類:

Vector force1(50, 60); Vector force2(10, 70); Vector net; net = force1 + force2;

返回的不是force1,也不是force2,force1和force2在這個過程中應該保持不變。因此,返回值不能是指向在調用函數中已經存在的對象的引用。相反,在Vector::operator+()中計算得到的兩個矢量的和被存儲在一個新的臨時對象中,該函數也不應返回指向該臨時對象的引用,而應該返回實際的Vector對象不是引用

Vector Vector::operator+(const Vector & b) const {return Vector(x + b.x, y + b.y); }

在這種情況下,存在調用復制構造函數來創建被返回的對象的開銷,然而這是無法避免的。

在上述示例中,構造函數調用Vector(x + b.x, y + b.y)創建一個方法operator+()能夠訪問的對象;而返回語句引發的對復制構造函數的隱式調用創建一個調用程序能夠訪問的對象。

?返回const對象

前面的Vector::operator+( )定義有一個奇異的屬性,它旨在讓您能夠以下面這樣的方式使用它:

net = force1 + force2; // 1:3個Vector對象 然而,這種定義也允許您這樣使用它: force1 + force2 = net; // 2:晦澀難懂的編程 cout << (force1 + force2 = net).magval() << endl; // 3:癲狂的編程

這提出了三個問題。為何編寫這樣的語句?這些語句為何可行?這些語句有何功能?

首先,沒有要編寫這種語句的合理理由,但并非所有代碼都是合理的。即使是程序員也會犯錯。例如,為Vector類定義operator==()時,您可能錯誤地輸入這樣的代碼:

if (force1 + force2 = net) 而不是: if (force1 + force2 == net)

另外,程序員通常很有創意,這可能導致錯誤。

其次,這種代碼之所以可行,是因為復制構造函數將創建一個臨時對象來表示返回值。因此,在前面的代碼中,表達式force1 + force2的結果為一個臨時對象。在語句1中,該臨時對象被賦給net;在語句2和3中,net被賦給該臨時對象。

第三,使用完臨時對象后,將把它丟棄。例如,對于語句2,程序計算force1和force2之和,將結果復制到臨時返回對象中,再用net的內容覆蓋臨時對象的內容,然后將該臨時對象丟棄。原來的矢量全都保持不變。語句3顯示臨時對象的長度,然后將其刪除。

如果您擔心這種行為可能引發的誤用和濫用,有一種簡單的解決方案:將返回類型聲明為const Vector。例如,如果Vector::operator+()的返回類型被聲明為const Vector,則語句1仍然合法,但語句2和語句3將是非法的。

總之,如果方法或函數要返回局部對象,則應返回對象,而不是指向對象的引用。在這種情況下,將使用復制構造函數來生成返回的對象。如果方法或函數要返回一個沒有公有復制構造函數的類(如ostream類)的對象,它必須返回一個指向這種對象的引用。最后,有些方法和函數(如重載的賦值運算符)可以返回對象,也可以返回指向對象的引用,在這種情況下,應首選引用,因為其效率更高。

C++程序經常使用指向對象的指針,因此,這里來練習一下。程序清單saying1.cpp使用數組索引值來跟蹤最短的字符串和按字母順序排在最前面的字符串。另一種方法是使用指針指向這些類別的開始位置,下面的程序清單使用兩個指向String的指針實現了這種方法。最初,shortest指針指向數組中的第一個對象。每當程序找到比指向的字符串更短的對象時,就把shortest重新設置為指向該對象。同樣,first指針跟蹤按字母順序排在最前面的字符串。這兩個指針并不創建新的對象,而只是指向已有的對象。因此,這些指針并不要求使用new來分配內存。

除此之外,程序清單中的程序還使用一個指針來跟蹤新對象:

String * favorite = new String(sayings[choice]);

這里指針favorite指向new創建的未被命名對象。這種特殊的語法意味著使用對象saying[choice]來初始化新的String對象,這將調用復制構造函數,因為復制構造函數(const String &)的參數類型與初始化值(saying[choice])匹配。程序使用srand()、rand()和time()隨機選擇一個值。

// sayings2.cpp -- using pointers to objects // compile with string1.cpp #include <cstdlib> // (or stdlib.h) for rand(), srand() #include <ctime> // (or time.h) for time() #include <iostream> #include "string1.h" const int ArSize = 10; const int MaxLen = 81; int main() {using namespace std;String name;cout << "Hi, what's your name?\n>> ";cin >> name;cout << name << ", please enter up to " << ArSize<< " short sayings <empty line to quit>:\n";String sayings[ArSize];char temp[MaxLen]; // temporary string storageint i;for (i = 0; i < ArSize; i++) {cout << i + 1 << ": ";cin.get(temp, MaxLen);while (cin && cin.get() != '\n') continue;if (!cin || temp[0] == '\0') // empty line?break; // i not incrementedelsesayings[i] = temp; // overloaded assignment}int total = i; // total # of lines readif (total > 0) {cout << "Here are your sayings:\n";for (i = 0; i < total; i++) cout << sayings[i] << "\n";// use pointers to keep track of shortest, first stringsString* shortest = &sayings[0]; // initialize to first objectString* first = &sayings[0];for (i = 1; i < total; i++) {if (sayings[i].length() < shortest->length()) shortest = &sayings[i];if (sayings[i] < *first) // compare valuesfirst = &sayings[i]; // assign address}cout << "Shortest saying:\n" << *shortest << endl;cout << "First alphabetically:\n" << *first << endl;srand(time(0));int choice = rand() % total; // pick index at random// use new to create, initialize new String objectString* favorite = new String(sayings[choice]);cout << "My favorite saying:\n" << *favorite << endl;delete favorite;} elsecout << "Not much to say, eh?\n";cout << "Bye.\n";return 0; }

通常,如果Class_name是類,value的類型為Type_name,則下面的語句:

Class_name * pclass = new Class_name(value);

將調用如下構造函數:

Class_name(Type_name);

這里可能還有一些瑣碎的轉換,例如:

Class_name(const Type_name &);

另外,如果不存在二義性,則將發生由原型匹配導致的轉換(如從int到double)。下面的初始化方式將調用默認構造函數:

Class_name * ptr = new Class_name; String(): num_string++ = 1:# Hi, what's your name? >> |Kirt Rood Kirt Rood, please enter up to 10 short sayings <empty line to quit>: String(): num_string++ = 2:# String(): num_string++ = 3:# String(): num_string++ = 4:# String(): num_string++ = 5:# String(): num_string++ = 6:# String(): num_string++ = 7:# String(): num_string++ = 8:# String(): num_string++ = 9:# String(): num_string++ = 10:# String(): num_string++ = 11:# 1: |a friend in need is a friend indeed 2: |neighter a borrower nor a lender be 3: |a stitch in time saves nine 4: |a niche in time saves stine 5: |it takes a crook to catch a crook 6: |cold hands, warm heart 7: |<Enter> Here are your sayings: a friend in need is a friend indeed neighter a borrower nor a lender be a stitch in time saves nine a niche in time saves stine it takes a crook to catch a crook cold hands, warm heart Shortest saying: cold hands, warm heart First alphabetically: a friend in need is a friend indeed String(const String& st): num_string++ = 12:a niche in time saves stine# My favorite saying: a niche in time saves stine object:"a niche in time saves stine" deleted. Bye. object:"" deleted. object:"" deleted. object:"" deleted. object:"" deleted. object:"cold hands, warm heart" deleted. object:"it takes a crook to catch a crook" deleted. object:"a niche in time saves stine" deleted. object:"a stitch in time saves nine" deleted. object:"neighter a borrower nor a lender be" deleted. object:"a friend in need is a friend indeed" deleted. object:"Kirt Rood" deleted.

?再談new和delete

首先,它使用new為創建的每一個對象的名稱字符串分配存儲空間,這是在構造函數中進行的,因此析構函數使用delete來釋放這些內存。因為字符串是一個字符數組,所以析構函數使用的是帶中括號的delete。這樣,當對象被釋放時,用于存儲字符串內容的內存將被自動釋放。上面程序清單中的代碼使用new來為整個對象分配內存:

String * favorite = new String(sayings[choice]);

這不是為要存儲的字符串分配內存,而是為對象分配內存;也就是說,為保存字符串地址的str指針和len成員分配內存(程序并沒有給num_string成員分配內存,這是因為num_string成員是靜態成員,它獨立于對象被保存)。創建對象將調用構造函數,后者分配用于保存字符串的內存,并將字符串的地址賦給str。然后,當程序不再需要該對象時,使用delete刪除它。對象是單個的,因此,程序使用不帶中括號的delete。與前面介紹的相同,這將只釋放用于保存str指針和len成員的空間,并不釋放str指向的內存,而該任務將由析構函數來完成(參見下圖)。

調用析構函數

在下述情況下析構函數將被調用:

  • 如果對象是自動變量automatic object,則當執行完定義該對象的程序塊時,將調用該對象的析構函數。
  • 如果對象是靜態變量(外部、靜態、靜態外部或來自名稱空間),則在程序結束時將調用對象的析構函數。
  • 如果對象是用new創建的dynamic object,則僅當您顯式使用delete刪除對象時,其析構函數才會被調用。

?指針和對象小結

使用對象指針時,需要注意幾點(參見下圖):

指針和對象 使用new創建對象

?再談定位new運算符

前面介紹過,定位new運算符讓您能夠在分配內存時能夠指定內存位置。第9章從內置類型的角度討論了定位new運算符,將這種運算符用于對象時情況有些不同,下面的程序清單使用了定位new運算符和常規new運算符給對象分配內存,其中定義的類的構造函數和析構函數都會顯示一些信息,讓用戶能夠了解對象的歷史。

// placenew1.cpp -- new, placement new, no delete #include <iostream> #include <new> #include <string> using namespace std; const int BUF = 512; class JustTesting { private:string words;int number; public:JustTesting(const string& s = "Just Testing", int n = 0) {words = s;number = n;cout << words << " constructed\n";}~JustTesting() {cout << words << " destroyed\n";}void Show() const {cout << words << ", " << number << endl;} }; int main() {char* buffer = new char[BUF]; // get a block of memoryJustTesting* pc1, * pc2;pc1 = new (buffer) JustTesting; // place object in bufferpc2 = new JustTesting("Heap1", 20); // place object on heapcout << "Memory block addresses:\n"<< "buffer: " << (void*)buffer << " heap: " << pc2 << endl;cout << "Memory contents:\n";cout << pc1 << ": ";pc1->Show();cout << pc2 << ": ";pc2->Show();JustTesting* pc3, * pc4;pc3 = new (buffer) JustTesting("Bad Idea", 6);pc4 = new JustTesting("Heap2", 10);cout << "Memory contents:\n";cout << pc3 << ": ";pc3->Show();cout << pc4 << ": ";pc4->Show();delete pc2; // free Heap1delete pc4; // free Heap2delete[] buffer; // free buffercout << "Done\n";return 0; } Just Testing constructed Heap1 constructed Memory block addresses: buffer: 00825EE8 heap: 00826B48 Memory contents: 00825EE8: Just Testing, 0 00826B48: Heap1, 20 Bad Idea constructed Heap2 constructed Memory contents: 00825EE8: Bad Idea, 6 0081F448: Heap2, 10 Heap1 destroyed Heap2 destroyed Done

程序清單在使用定位new運算符時存在兩個問題。首先,在創建第二個對象(pc3對應的)時,定位new運算符使用一個新對象來覆蓋用于第一個對象(pc1對應的)的內存單元。顯然,如果類動態地為其成員分配內存,這將引發問題。

其次,將delete用于pc2和pc4時,將自動調用為pc2和pc4指向的對象調用析構函數;然而,將delete[]用于buffer時,不會為使用定位new運算符創建的對象調用析構函數。

這里的經驗教訓與第9章介紹的相同:程序員必須負責管用定位new運算符用從中使用的緩沖區內存單元。要使用不同的內存單元,程序員需要提供兩個位于緩沖區的不同地址,并確保這兩個內存單元不重疊。例如,可以這樣做:

pc1 = new (buffer) JustTesting; pc3 = new (buffer + sizeof (JustTesting)) JustTesting("Better Idea", 6);

其中指針pc3相對于pc1的偏移量為JustTesting對象的大小。

第二個教訓是,如果使用定位new運算符來為對象分配內存,必須確保其析構函數被調用。但如何確保呢?對于在堆中創建的對象,可以這樣做:

delete pc2; // 刪除pc2指向的對象 但不能像下面這樣做: delete pc1; // 刪除pc1指向的對象?NO! delete pc3; // 刪除pc3指向的對象?NO!

原因在于delete可與常規new運算符配合使用,但不能與定位new運算符配合使用。例如,指針pc3沒有收到new運算符返回的地址(通過打印信息可知pc3地址就等于buffer地址),因此delete pc3將導致運行階段錯誤。在另一方面,指針pc1指向的地址與buffer相同,但buffer是使用new []初始化的,因此必須使用delete []而不是delete來釋放。即使buffer是使用new而不是new []初始化的,delete pc1也將釋放buffer,而不是pc1。這是因為new/delete系統知道已分配的512字節塊buffer,但對定位new運算符對該內存塊做了何種處理一無所知。

該程序確實釋放了buffer:delete [] buffer;

正如上述注釋指出的,delete [] buffer;釋放使用常規new運算符分配的整個內存塊,但它沒有為定位new運算符在該內存塊中創建的對象調用析構函數。您之所以知道這一點,是因為該程序使用了一個顯示信息的析構函數,該析構函數宣布了“Heap1”和“Heap2”的死亡,但卻沒有宣布“Just Testing”和“Bad Idea”的死亡。

這種問題的解決方案是,顯式地為使用定位new運算符創建的對象調用析構函數。正常情況下將自動調用析構函數,這是需要顯式調用析構函數的少數幾種情形之一。顯式地調用析構函數時,必須指定要銷毀的對象。由于有指向對象的指針,因此可以使用這些指針:

pc3->~JustTesting(); // 銷毀pc3指向的對象 pc1->~JustTesting(); // 銷毀pc1指向的對象

下面程序清單對定位new運算符使用的內存單元進行管理,加入到合適的delete和顯式析構函數調用,從而修復了上面程序清單中的問題。需要注意的一點是正確的刪除順序。對于使用定位new運算符創建的對象,應以與創建順序相反的順序進行刪除。原因在于,晚創建的對象可能依賴于早創建的對象。另外,僅當所有對象都被銷毀后,才能釋放用于存儲這些對象的緩沖區。

// placenew2.cpp -- new, placement new, no delete #include <iostream> #include <string> #include <new> using namespace std; const int BUF = 512; class JustTesting { private:string words;int number; public:JustTesting(const string& s = "Just Testing", int n = 0) {words = s; number = n; cout << words << " constructed\n";}~JustTesting() {cout << words << " destroyed\n";}void Show() const {cout << words << ", " << number << endl;} }; int main() {char* buffer = new char[BUF]; // get a block of memoryJustTesting* pc1, * pc2;pc1 = new (buffer) JustTesting; // place object in bufferpc2 = new JustTesting("Heap1", 20); // place object on heapcout << "Memory block addresses:\n" << "buffer: "<< (void*)buffer << " heap: " << pc2 << endl;cout << "Memory contents:\n";cout << pc1 << ": ";pc1->Show();cout << pc2 << ": ";pc2->Show();JustTesting* pc3, * pc4;// fix placement new locationpc3 = new (buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6);pc4 = new JustTesting("Heap2", 10);cout << "Memory contents:\n";cout << pc3 << ": ";pc3->Show();cout << pc4 << ": ";pc4->Show();delete pc2; // free Heap1 delete pc4; // free Heap2// explicitly destroy placement new objectspc3->~JustTesting(); // destroy object pointed to by pc3pc1->~JustTesting(); // destroy object pointed to by pc1delete[] buffer; // free bufferreturn 0; } Just Testing constructed Heap1 constructed Memory block addresses: buffer: 010880F8 heap: 010844E8 Memory contents: 010880F8: Just Testing, 0 010844E8: Heap1, 20 Better Idea constructed Heap2 constructed Memory contents: 01088118: Better Idea, 6 0108CF48: Heap2, 10 Heap1 destroyed Heap2 destroyed Better Idea destroyed Just Testing destroyed

從結果可以看到"Better Idea"相對于"Just Testing"地址偏移了32位,而sizeof(JustTesting)==32。在第二次調用定位new運算符時,提供了一個從數組buffer開頭算起的偏移量offset==32bit,即將JustTesting("Better Idea")放入到buffer緩沖區地址+32bit開始的地方。該程序使用定位new運算符在相鄰的內存單元中創建兩個對象,并調用了合適的析構函數。

注:在Win32系統下,C++ string類型默認占28字節,int占4字節,所以sizeof(JustTesting)==32;而sizeof(buffer)或sizeof(new char[1024])==4這是為什么?這就涉及動態分配與靜態分配內存了。如果數組是動態生成,得到的就是指針的長度;如果數組是靜態生成的,則可以得出數組的大小。即是說:

char?buffer[512]; cout << sizeof(buffer); // 顯示:512 而: char?*buffer=new?char[512]; cout << sizeof(buffer); // 顯示:4

由于是動態分配的內存,編譯器怎么知道它的大小呢?所以不能對指針用sizeof來得到分配的內存大小,此時用sizeof得到的僅僅是指針占用的內存字節數。記住:用new動態申請如果需要知道大小的話,只能自己記住,編譯器不會幫你記的。

復習各種技術

?重載<<運算符

要重新定義 << 運算符,以便將它和cout一起用來顯示對象的內容,請定義下面的友元運算符函數:

ostream & operator<<(ostream & os, const class_Name & obj) {os << ... ; // 顯示對象信息return os; }

如果該類提供了能夠返回所需內容的公有方法,則可在運算符函數中使用這些方法,這樣便不用將它們設置為友元函數了。

?轉換函數

要將單個值轉換為類類型,需要創建原型如下所示的類構造函數:

class_Name(type_name value);

其中,class_Name為類名,type_name是要轉換的類型的名稱。

要將類轉換為其他類型,需要創建原型如下所示的類成員函數:

operator type_name();

雖然該函數沒有聲明返回類型,但應返回所需類型的值。

使用轉換函數時要小心。可以在聲明構造函數時使用關鍵字explicit,以防止它被用于隱式轉換。

?其構造函數使用new的類

如果類使用new運算符來分配類成員指向的內存,在設計時應采取一些預防措施(前面總結了這些預防措施,應牢記這些規則,這是因為編譯器并不知道這些規則,因此無法發現錯誤)。

  • 對于指向的內存是由new分配的所有類成員,都應在類的析構函數中對其使用delete,該運算符將釋放分配的內存。
  • 如果析構函數通過對指針類成員使用delete來釋放內存,則每個構造函數都應當使用new來初始化指針,或將它設置為空指針。
  • 構造函數中要么使用new [],要么使用new,而不能混用。如果構造函數使用的是new[],則析構函數應使用delete [];如果構造函數使用的是new,則析構函數應使用delete。
  • 應定義一個分配內存(而不是將指針指向已有內存)的復制構造函數。這樣程序將能夠將類對象初始化為另一個類對象。這種構造函數的原型通常如:className(const className &)
  • 應定義一個重載賦值運算符的類成員函數,其函數定義如下(其中c_pointer是c_name的類成員,類型為指向type_name的指針)。下面的示例假設使用new []來初始化變量c_pointer:
c_name & c_name::operator=(const c_name & cn) {if (this == &cn) {return *this;}delete [] c_pointer;// 設置type_name類型單元的大小sizec_pointer = new type_name[size];// 拷貝cn.c_pointer指向的數據到c_pointer指向的位置...return *this; }

隊列模擬

Heather銀行打算在Food Heap超市開設一個自動柜員機(ATM)。Food Heap超市的管理者擔心排隊等待使用ATM的人流會干擾超市的交通,希望限制排隊等待的人數。Heather銀行希望對顧客排隊等待的時間進行估測。要編寫一個程序來模擬這種情況,讓超市的管理者可以了解ATM可能造成的影響。

隊列中的項目是顧客。Heather銀行的代表介紹:通常,三分之一的顧客只需要一分鐘便可獲得服務,三分之一的顧客需要兩分鐘,另外三分之一的顧客需要三分鐘。另外,顧客到達的時間是隨機的,但每個小時使用自動柜員機的顧客數量相當穩定。工程的另外兩項任務是:設計一個表示顧客的類;編寫一個程序來模擬顧客和隊列之間的交互(參見下圖)。

?隊列類

首先需要設計一個Queue類。這里先列出隊列的特征:

  • 隊列存儲有序的項目序列;
  • 隊列所能容納的項目數有一定的限制;
  • 應當能夠創建空隊列;
  • 應當能夠檢查隊列是否為空;
  • 應當能夠檢查隊列是否是滿的;
  • 應當能夠在隊尾添加項目;
  • 應當能夠從隊首刪除項目;
  • 應當能夠確定隊列中項目數。

設計類時,需要開發公有接口和私有實現。

①Queue類的接口

從隊列的特征可知,Queue類的公有接口應該如下:

class Queue {enum {Q_SIZE = 10}; private: // private representation to be developed later public:Queue(int qs = Q_SIZE); // 創建一個默認qs限制大小的隊列~Queue();bool isempty() const;bool isfull() const;int queuecount() const;bool enqueue(const Item &item); // 在隊尾添加itembool dequeue(Item &item); // 從隊頭刪除item };

構造函數創建一個空隊列。默認情況下,隊列最多可存儲10個項目,但是可以用顯式初始化參數覆蓋該默認值:

Queue line1; // queue with 10-item limit Queue line2(20); // queue with 20-item limit

使用隊列時,可以使用typedef來定義Item(第14章將介紹如何使用類模板)。

②Queue類的實現

確定接口后,便可以實現它。首先,需要確定如何表示隊列數據。一種方法是使用new動態分配一個數組,它包含所需的元素數。然而,對于隊列操作而言,數組并不太合適。例如,刪除數組的第一個元素后,需要將余下的所有元素向前移動一位;否則需要作一些更費力的工作,如將數組視為是循環的。然而,鏈表能夠很好地滿足隊列的要求。鏈表由節點序列構成。每一個節點中都包含要保存到鏈表中的信息以及一個指向下一個節點的指針。對于這里的隊列來說,數據部分都是一個Item類型的值,因此可以使用下面的結構來表示節點:

struct Node {Item item; // 存儲在node中的數據struct Node * next; // 指向下一個節點 };

如下左圖所示是一個單向鏈表,因為每個節點都只包含一個指向其他節點的指針。知道第一個節點的地址后,就可以沿指針找到后面的每一個節點。通常,鏈表最后一個節點中的指針被設置為NULL(或0),以指出后面沒有節點了。在C++11中,應使用新增的關鍵字nullptr。要跟蹤鏈表,必須知道第一個節點的地址。可以讓Queue類的一個數據成員指向鏈表的起始位置。具體地說,這是所需要的全部信息,有了這種信息后,就可以沿節點鏈找到任何節點。然而,由于隊列總是將新項目添加到隊尾,因此包含一個指向最后一個節點的數據成員將非常方便(參見下右圖)。此外,還可以使用數據成員來跟蹤隊列可存儲的最大項目數以及當前的項目數。所以,類聲明的私有部分與下面類似:

[鏈表]? ??[Queue對象]

class Queue { private:struct Node {Item item;struct Node * next;}enum {Q_SIZE = 10};Node * front;Node * rear;int items; // 當前隊列元素個數const int qsize; // 隊列允許的最大元素個數... public:... };

上述聲明使用了C++的一項特性:在類中嵌套結構或類聲明。通過將Node聲明放在Queue類中,可以使其作用域為整個類。也就是說,Node是這樣一種類型:可以使用它來聲明類成員,也可以將它作為類方法中的類型名稱,但只能在類中使用。這樣,就不必擔心該Node聲明與某些全局聲明或其他類中聲明的Node發生沖突。有些較老的編譯器不支持嵌套的結構和類,如果您的編譯器是這樣的,則必須將Node結構定義為全局的,將其作用域設置為整個文件。

在類聲明中聲明的結構、類或枚舉被稱為是被嵌套在類中,其作用域為整個類。這種聲明不會創建數據對象,而只是指定了可以在類中使用的類型。如果聲明是在類的私有部分進行的,則只能在這個類使用被聲明的類型;如果聲明是在公有部分進行的,則可以從類的外部通過作用域解析運算符使用被聲明的類型。例如,如果Node是在Queue類的公有部分聲明的,則可以在類的外面聲明Queue::Node類型的變量。

③類方法

類構造函數應提供類成員的值。由于在這個例子中,隊列最初是空的,因此隊首和隊尾指針都設置為NULL(0或nullptr),并將items設置為0。另外,還應將隊列的最大長度qsize設置為構造函數參數qs的值。下面的實現方法無法正常運行:

Queue::Queue(int qs) {front = rear = NULL;items = 0;qsize = qs; // 不可接收的! }

問題在于qsize是常量,所以可以對它進行初始化,但不能給它賦值。從概念上說,調用構造函數時,對象將在括號中的代碼執行之前被創建。因此,調用Queue(int qs)構造函數將導致程序首先給4個成員變量分配內存。然后,程序流程進入到括號中,使用常規的賦值方式將值存儲到內存中。因此,對于const數據成員,必須在執行到構造函數體之前,即創建對象時進行初始化。C++提供了一種特殊的語法來完成上述工作,它叫做成員初始化列表(member initializer list)。成員初始化列表由逗號分隔的初始化列表組成(前面帶冒號)。它位于參數列表的右括號之后、函數體左括號之前。如果數據成員的名稱為mdata,并需要將它初始化為val,則初始化器為mdata(val)。使用這種表示法,可以這樣編寫Queue的構造函數:

Queue::Queue(int qs) : qsize(qs) { // 將qsize初始化為qsfront = rear = NULL;items = 0; }

通常,初值可以是常量或構造函數的參數列表中的參數。這種方法并不限于初始化常量,可以將Queue構造函數寫成如下所示:

Queue::Queue(int qs) : qsize(qs), front(NULL), rear(NULL), items(0) { }

只有構造函數可以使用這種初始化列表語法。如上所示,對于const類成員,必須使用這種語法。另外,對于被聲明為引用的類成員,也必須使用這種語法:

class Agency {...}; class Agent { private:Agency & belong; // 必須通過初始化列表初始化之... }; Agent::Agent(Agency & a) : belong(a) {...}

這是因為引用與const數據類似,只能在被創建時進行初始化。對于簡單數據成員(例如front和items),使用成員初始化列表和在函數體中使用賦值沒有什么區別。然而,正如第14章將介紹的,對于本身就是類對象的成員來說,使用成員初始化列表的效率更高。

成員初始化列表的語法:

如果Classy是一個類,而mem1、mem2和mem3都是這個類的數據成員,則類構造函數可以使用如下的語法來初始化數據成員:

Classy::Classy(int n, int m) : mem1(n), mem2(0), mem3(n * m + 2) { ... }

上述代碼將mem1初始化為n,將mem2初始化為0,將mem3初始化為n*m + 2。從概念上說,這些初始化工作是在對象創建時完成的,此時還未執行括號中的任何代碼。請注意以下幾點:

  • 這種格式只能用于構造函數

  • 必須用這種格式來初始化非靜態const數據成員(至少在C++11之前是這樣的)

  • 必須用這種格式來初始化引用數據成員

數據成員被初始化的順序與它們出現在類聲明中的順序相同,與初始化器中的排列順序無關。

不能將成員初始化列表語法用于構造函數之外的其他類方法。

成員初始化列表使用的括號方式也可用于常規初始化。也就是說,如果愿意,可以將下述代碼:

int games = 162; double talk = 2.71828; 替換為: int games(162); double talk(2.71828);

這使得初始化內置類型就像初始化類對象一樣。

C++11允許類內初始化:

C++11允許您以更直觀的方式進行初始化:

class Classy {int mem1 = 10;const int mem2 = 20;... }

這與在構造函數中使用成員初始化列表等價:

Classy::Classy() : mem1(10), mem2(20) {...}

成員mem1和mem2將分別被初始化為10和20,除非調用了使用成員初始化列表的構造函數,在這種情況下,實際列表將覆蓋這些默認初始值:

Classy::Classy(int n) : mem1(n) {...}

在這里,構造函數將使用n來初始化mem1,但mem2仍被設置為20。

isempty()、isfull()和queuecount()的代碼都非常簡單。如果items為0,則隊列是空的;如果items等于qsize,則隊列是滿的。要知道隊列中的項目數,只需返回items的值。后面的程序清單列出了這些代碼。

將項目添加到隊尾(入隊)比較麻煩。下面是一種方法:

bool Queue::enqueue(const Item & item) {if (isfull())return false;Node * add = new Node; // 創建節點// 如果失敗,拋出std::bad_alloc異常add->item = item; // 設置節點指針add->next = NULL; // 或nullptritems++;if (front == NULL)front = add; // 放在隊首elserear->next = add; // 放在隊尾rear = add; // rear指向新節點return true; }

總之,方法需要經過下面幾個階段(如下圖):

將項目入隊

刪除隊首項目(出隊)也需要多個步驟才能完成。下面是一種方式:

bool Queue::dequeue(Item & item) {if (front == NULL)return false;item = front->item; // 設置item為隊列首項items--;Node * temp = front; // 保存首項的地址front = front->next; // 重置首項讓成為下一個項delete temp; // 刪除先前的首項if (items == 0)rear = NULL;return true; }

總之,需要經過下面幾個階段(如下圖):

將項目出隊

是否需要其他方法呢?類構造函數沒有使用new,所以乍一看,好像不用理會由于在構造函數中使用new給類帶來的特殊要求。當然,這種印象是錯誤的,因為向隊列中添加對象將調用new來創建新的節點。通過刪除節點的方式,dequeue( )方法確實可以清除節點,但這并不能保證隊列在到期時為空。因此,類需要一個顯式析構函數——該函數刪除剩余的所有節點。下面是一種實現,它從鏈表頭開始,依次刪除其中的每個節點:

Queue::~Queue() {Node * temp;while (front != NULL) {temp = front;front = front->next;delete temp;} }

您知道,使用new的類通常需要包含顯式復制構造函數和執行深度復制的賦值運算符,這個例子也是如此嗎?首先要回答的問題是,默認的成員復制是否合適?答案是否定的。復制Queue對象的成員將生成一個新的對象,該對象指向鏈表原來的頭和尾。因此,將項目添加到復制的Queue對象中,將修改共享的鏈表。這樣做將造成非常嚴重的后果。更糟的是,只有副本的尾指針得到了更新,從原始對象的角度看,這將損壞鏈表。顯然,要克隆或復制隊列,必須提供復制構造函數和執行深度復制的賦值構造函數。

當然,這提出了這樣一個問題:為什么要復制隊列呢?也許是希望在模擬的不同階段保存隊列的瞬像,也可能是希望為兩個不同的策略提供相同的輸入。實際上,擁有拆分隊列的操作是非常有用的,超市在開設額外的收款臺時經常這樣做。同樣,也可能希望將兩個隊列結合成一個或者截短一個隊列。

但假設這里的模擬不實現上述功能。難道不能忽略這些問題,而使用已有的方法嗎?當然可以。然而,在將來的某個時候,可能需要再次使用隊列且需要復制。另外,您可能會忘記沒有為復制提供適當的代碼。在這種情況下,程序將能編譯和運行,但結果卻是混亂的,甚至會崩潰。因此,最好還是提供復制構造函數和賦值運算符,盡管目前并不需要它們。

幸運的是,有一種小小的技巧可以避免這些額外的工作,并確保程序不會崩潰。這就是將所需的方法定義為偽私有方法:

class Queue { private:Queue(const Queue & q) : qsize(0) {}Queue & operator=(const Queue & q) { return *this; }... };

這樣做有兩個作用:第一,它避免了本來將自動生成的默認方法定義。第二,因為這些方法是私有的,所以不能被廣泛使用。也就是說,如果nip和tuck是Queue對象,則編譯器就不允許這樣做:

Queue snick(nip); // 不允許 tuck = nip; // 不允許

所以,與其將來面對無法預料的運行故障,不如得到一個易于跟蹤的編譯錯誤,指出這些方法是不可訪問的。另外,在定義其對象不允許被復制的類時,這種方法也很有用。

C++11提供了另一種禁用方法的方式——使用關鍵字delete,這將在第18章介紹。

還有沒有其他影響需要注意呢?當然有。當對象被按值傳遞(或返回)時,復制構造函數將被調用。然而,如果遵循優先采用按引用傳遞對象的慣例,將不會有任何問題。另外,復制構造函數還被用于創建其他的臨時對象,但Queue定義中并沒有導致創建臨時對象的操作,例如重載加法運算符。

?Custormer類

接下來需要設計客戶類。通常,ATM客戶有很多屬性,例如姓名、賬戶和賬戶結余。然而,這里的模擬需要使用的唯一一個屬性是客戶何時進入隊列以及客戶交易所需的時間。當模擬生成新客戶時,程序將創建一個新的客戶對象,并在其中存儲客戶的到達時間以及一個隨機生成的交易時間。當客戶到達隊首時,程序將記錄此時的時間,并將其與進入隊列的時間相減,得到客戶的等候時間。下面的代碼演示了如何定義和實現Customer類:

class Customer { private:long arrive; // 客戶到達的時間int processtime; // 客戶操作ATM需要的時間 public:Customer() { arrive = processtime = 0; }void set(long when);long when() const { return arrive; }int ptime const { return processtime; } }; void Customer::set(long when) {processtime = std::rand() % 3 + 1;arrive = when; }

默認構造函數創建一個空客戶。set()成員函數將到達時間設置為參數,并將處理時間設置為1~3中的一個隨機值。

// queue.h -- interface for a queue #ifndef QUEUE_H_ #define QUEUE_H_ // This queue will contain Customer items class Customer { private:long arrive; // arrival time for customerint processtime; // processing time for customer public:Customer() : arrive(0), processtime(0) {}void set(long when);long when() const {return arrive;}int ptime() const {return processtime;} }; typedef Customer Item; class Queue { private:// class scope definitions// Node is a nested structure definition local to this classstruct Node {Item item;struct Node* next;};enum {Q_SIZE = 10};// private class membersNode* front; // pointer to front of QueueNode* rear; // pointer to rear of Queueint items; // current number of items in Queueconst int qsize; // maximum number of items in Queue// preemptive definitions to prevent public copyingQueue(const Queue& q) : qsize(0) {}Queue& operator=(const Queue& q) {return *this;} public:Queue(int qs = Q_SIZE); // create queue with a qs limit~Queue();bool isempty() const;bool isfull() const;int queuecount() const;bool enqueue(const Item& item); // add item to endbool dequeue(Item& item); // remove item from front }; #endif // queue.cpp -- Queue and Customer methods #include "queue.h" #include <cstdlib> // (or stdlib.h) for rand() // Queue methods Queue::Queue(int qs) : qsize(qs) {front = rear = NULL; // or nullptritems = 0; } Queue::~Queue() {Node* temp;while (front != NULL) { // while queue is not yet emptytemp = front; // save address of front itemfront = front->next;// reset pointer to next itemdelete temp; // delete former front} } bool Queue::isempty() const {return items == 0; } bool Queue::isfull() const {return items == qsize; } int Queue::queuecount() const {return items; } // Add item to queue bool Queue::enqueue(const Item& item) {if (isfull())return false;Node* add = new Node; // create node// on failure, new throws std::bad_alloc exceptionadd->item = item; // set node pointersadd->next = NULL; // or nullptr;items++;if (front == NULL) // if queue is empty,front = add; // place item at frontelserear->next = add; // else place at rearrear = add; // have rear point to new nodereturn true; } // Place front item into item variable and remove from queue bool Queue::dequeue(Item& item) {if (front == NULL)return false;item = front->item; // set item to first item in queueitems--;Node* temp = front; // save location of first itemfront = front->next; // reset front to next itemdelete temp; // delete former first itemif (items == 0)rear = NULL;return true; } // customer method // when is the time at which the customer arrives // the arrival time is set to when and the processing // time set to a random value in the range 1 - 3 void Customer::set(long when) {processtime = std::rand() % 3 + 1;arrive = when; }

?ATM模擬

程序允許用戶輸入3個數:隊列的最大長度、程序模擬的持續時間(單位為小時)以及平均每小時的客戶數。程序將使用循環——每次循環代表一分鐘。在每分鐘的循環中,程序將完成下面的工作。

1.判斷是否來了新的客戶。如果來了,并且此時隊列未滿,則將它添加到隊列中,否則拒絕客戶入隊。

2.如果沒有客戶在進行交易,則選取隊列的第一個客戶。確定該客戶的已等候時間,并將wait_time計數器設置為新客戶所需的處理時間。

3.如果客戶正在處理中,則將wait_time計數器減1。

4.記錄各種數據,如獲得服務的客戶數目、被拒絕的客戶數目、排隊等候的累積時間以及累積的隊列長度等。

當模擬循環結束時,程序將報告各種統計結果。

程序將使用下面的函數來確定是否在循環期間有客戶到來:

bool newcustomer(double x) {return (std::rand() * x / RAND_MAX < 1); }

其工作原理如下:值RAND_MAX是在cstdlib文件(以前是stdlib.h)中定義的,是rand( )函數可能返回的最大值(0是最小值)。假設客戶到達的平均間隔時間x為6,則rand( )* x /RAND_MAX的值將位于0到6之間。具體地說,平均每隔6次,這個值會有1次小于1。然而,這個函數可能會導致客戶到達的時間間隔有時為1分鐘,有時為20分鐘。這種方法雖然很笨拙,但可使實際情況不同于有規則地每6分鐘到來一個客戶。如果客戶到達的平均時間間隔少于1分鐘,則上述方法將無效,但模擬并不是針對這種情況設計的。

// bank.cpp -- using the Queue interface // compile with queue.cpp #include <iostream> #include <cstdlib> // for rand() and srand() #include <ctime> // for time() #include "queue.h" const int MIN_PER_HR = 60; bool newcustomer(double x); // is there a new customer? int main() {using std::cin;using std::cout;using std::endl;using std::ios_base;// setting things upstd::srand(std::time(0)); // random initializing of rand()cout << "Case Study: Bank of Heather Automatic Teller\n";cout << "Enter maximum size of queue: ";int qs;cin >> qs;Queue line(qs); // line queue holds up to qs peoplecout << "Enter the number of simulation hours: ";int hours; // hours of simulationcin >> hours;// simulation will run 1 cycle per minutelong cyclelimit = MIN_PER_HR * hours; // # of cyclescout << "Enter the average number of customers per hour: ";double perhour; // average # of arrival per hourcin >> perhour;double min_per_cust; // average time between arrivalsmin_per_cust = MIN_PER_HR / perhour;Item temp; // new customer datalong turnaways = 0; // turned away by full queuelong customers = 0; // joined the queuelong served = 0; // served during the simulationlong sum_line = 0; // cumulative line lengthint wait_time = 0; // time until autoteller is freelong line_wait = 0; // cumulative time in line// running the simulationfor (int cycle = 0; cycle < cyclelimit; cycle++) {if (newcustomer(min_per_cust)) { // have newcomerif (line.isfull())turnaways++;else {customers++;temp.set(cycle); // cycle = time of arrivalline.enqueue(temp); // add newcomer to line}}if (wait_time <= 0 && !line.isempty()) {line.dequeue(temp); // attend next customerwait_time = temp.ptime(); // for wait_time minutesline_wait += cycle - temp.when();served++;}if (wait_time > 0)wait_time--;sum_line += line.queuecount();}// reporting resultsif (customers > 0) {cout << "customers accepted: " << customers << endl;cout << " customers served: " << served << endl;cout << " turnaways: " << turnaways << endl;cout << "average queue size: ";cout.precision(2);cout.setf(ios_base::fixed, ios_base::floatfield);cout << (double)sum_line / cyclelimit << endl;cout << " average wait time: "<< (double)line_wait / served << " minutes\n";} elsecout << "No customers!\n";cout << "Done!\n";return 0; } // x = average time, in minutes, between customers // return value is true if customer shows up this minute bool newcustomer(double x) {return (std::rand() * x / RAND_MAX < 1); } Case Study: Bank of Heather Automatic Teller Enter maximum size of queue: |10 Enter the number of simulation hours: |100 Enter the average number of customers per hour: |15 customers accepted: 1546customers served: 1545turnaways: 0 average queue size: 0.17average wait time: 0.65 minutes Done! Case Study: Bank of Heather Automatic Teller Enter maximum size of queue: |20 Enter the number of simulation hours: |100 Enter the average number of customers per hour: |30 customers accepted: 2990customers served: 2984turnaways: 25 average queue size: 8.01average wait time: 16.10 minutes Done! Case Study: Bank of Heather Automatic Teller Enter maximum size of queue: |10 Enter the number of simulation hours: |4 Enter the average number of customers per hour: |30 customers accepted: 117customers served: 117turnaways: 6 average queue size: 5.12average wait time: 10.50 minutes Done!

總結

以上是生活随笔為你收集整理的第12章-cpp类和动态内存分配的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

六月丁香婷婷色狠狠久久 | 蜜桃视频韩日免费播放 | 国产人妻人伦精品1国产丝袜 | 色综合久久88色综合天天 | 人妻熟女一区 | 久久精品国产99精品亚洲 | 2020久久超碰国产精品最新 | 亚洲色欲久久久综合网东京热 | 国产一精品一av一免费 | 久久综合九色综合欧美狠狠 | 图片小说视频一区二区 | 无遮无挡爽爽免费视频 | 亚洲第一无码av无码专区 | 成人av无码一区二区三区 | 久久人人爽人人人人片 | 全球成人中文在线 | 精品国偷自产在线视频 | 亚洲国产午夜精品理论片 | 久久综合香蕉国产蜜臀av | 国产免费久久久久久无码 | 国产成人午夜福利在线播放 | 小泽玛莉亚一区二区视频在线 | 国产精品无码一区二区桃花视频 | 国产真实夫妇视频 | 亚洲 a v无 码免 费 成 人 a v | 日本熟妇人妻xxxxx人hd | 欧美亚洲国产一区二区三区 | 日日碰狠狠躁久久躁蜜桃 | 小sao货水好多真紧h无码视频 | 国产婷婷色一区二区三区在线 | 久久精品丝袜高跟鞋 | 精品人妻中文字幕有码在线 | а√资源新版在线天堂 | 亚洲欧美中文字幕5发布 | 亚洲经典千人经典日产 | 精品日本一区二区三区在线观看 | 亚洲欧美日韩成人高清在线一区 | 东京无码熟妇人妻av在线网址 | 午夜精品久久久内射近拍高清 | 久久久精品欧美一区二区免费 | 中文字幕av伊人av无码av | 骚片av蜜桃精品一区 | 自拍偷自拍亚洲精品被多人伦好爽 | 天天躁日日躁狠狠躁免费麻豆 | 久久国产自偷自偷免费一区调 | 免费无码肉片在线观看 | 一本大道久久东京热无码av | 精品亚洲韩国一区二区三区 | 日本va欧美va欧美va精品 | 亚洲另类伦春色综合小说 | 国产成人一区二区三区在线观看 | 日本一区二区三区免费播放 | 成人精品视频一区二区三区尤物 | 午夜丰满少妇性开放视频 | 久久久精品欧美一区二区免费 | 久久综合九色综合97网 | 麻豆国产人妻欲求不满谁演的 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 日韩av激情在线观看 | 午夜丰满少妇性开放视频 | 久久成人a毛片免费观看网站 | 欧美真人作爱免费视频 | 性色欲情网站iwww九文堂 | 熟妇激情内射com | 好爽又高潮了毛片免费下载 | 欧美第一黄网免费网站 | 亚洲热妇无码av在线播放 | 丰满少妇熟乱xxxxx视频 | 国产成人一区二区三区在线观看 | 国产凸凹视频一区二区 | 亚洲人成无码网www | 正在播放东北夫妻内射 | 中文字幕乱妇无码av在线 | 亚洲综合无码久久精品综合 | 四虎国产精品免费久久 | 婷婷五月综合缴情在线视频 | 精品 日韩 国产 欧美 视频 | 久久精品人人做人人综合 | 午夜福利试看120秒体验区 | 亚洲熟熟妇xxxx | 亚洲 激情 小说 另类 欧美 | 亚洲日本一区二区三区在线 | 精品久久久无码人妻字幂 | 丰满岳乱妇在线观看中字无码 | 亚洲国产精品一区二区美利坚 | 亚洲精品美女久久久久久久 | 美女张开腿让人桶 | 天天躁夜夜躁狠狠是什么心态 | 黑森林福利视频导航 | 99久久人妻精品免费一区 | 人妻少妇精品久久 | 国产热a欧美热a在线视频 | 亚洲天堂2017无码中文 | 国产97在线 | 亚洲 | 日本一本二本三区免费 | 国产电影无码午夜在线播放 | 午夜性刺激在线视频免费 | www一区二区www免费 | 男人和女人高潮免费网站 | 少妇激情av一区二区 | 日本又色又爽又黄的a片18禁 | 丰满诱人的人妻3 | 99久久久无码国产精品免费 | 亚洲成av人片天堂网无码】 | 学生妹亚洲一区二区 | 欧美35页视频在线观看 | 久久人人爽人人爽人人片av高清 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 久久亚洲国产成人精品性色 | 内射巨臀欧美在线视频 | 日韩精品无码一区二区中文字幕 | 噜噜噜亚洲色成人网站 | 少妇性l交大片欧洲热妇乱xxx | 精品水蜜桃久久久久久久 | 亚洲中文字幕无码中文字在线 | 精品偷自拍另类在线观看 | 欧美日韩精品 | 国产在线aaa片一区二区99 | 中文字幕乱码亚洲无线三区 | 欧美35页视频在线观看 | 亚洲va欧美va天堂v国产综合 | 正在播放老肥熟妇露脸 | 骚片av蜜桃精品一区 | 亚洲国产一区二区三区在线观看 | 亚洲综合在线一区二区三区 | 高潮毛片无遮挡高清免费视频 | 国产人妻精品一区二区三区不卡 | 久久亚洲日韩精品一区二区三区 | 国产成人精品优优av | 99久久无码一区人妻 | 精品乱码久久久久久久 | 自拍偷自拍亚洲精品10p | 国产亚洲精品精品国产亚洲综合 | 欧美人与禽zoz0性伦交 | 国产va免费精品观看 | 国产凸凹视频一区二区 | 极品嫩模高潮叫床 | 国产免费无码一区二区视频 | 色一情一乱一伦一区二区三欧美 | 奇米影视888欧美在线观看 | 又大又硬又爽免费视频 | 国产精品成人av在线观看 | 天堂无码人妻精品一区二区三区 | 国产成人精品视频ⅴa片软件竹菊 | 人妻少妇精品视频专区 | 欧美性生交活xxxxxdddd | 国产精品怡红院永久免费 | 暴力强奷在线播放无码 | 精品国产国产综合精品 | 亚洲国产精品成人久久蜜臀 | 激情内射日本一区二区三区 | 一个人免费观看的www视频 | 国产麻豆精品精东影业av网站 | 日韩在线不卡免费视频一区 | 国产人成高清在线视频99最全资源 | 风流少妇按摩来高潮 | √天堂中文官网8在线 | 国产人妻精品午夜福利免费 | 我要看www免费看插插视频 | 国产精品高潮呻吟av久久 | 日韩人妻无码一区二区三区久久99 | 无遮挡啪啪摇乳动态图 | 亚洲成色www久久网站 | 国内揄拍国内精品少妇国语 | 在线成人www免费观看视频 | 精品国产一区二区三区四区在线看 | 国产精品内射视频免费 | 少妇被粗大的猛进出69影院 | 亚洲欧美综合区丁香五月小说 | 欧美日韩精品 | 少妇一晚三次一区二区三区 | 图片区 小说区 区 亚洲五月 | 日本乱人伦片中文三区 | 美女黄网站人色视频免费国产 | 在线播放亚洲第一字幕 | 国产9 9在线 | 中文 | 无码人妻av免费一区二区三区 | 高潮喷水的毛片 | 国产综合在线观看 | 强奷人妻日本中文字幕 | 老子影院午夜伦不卡 | 一本一道久久综合久久 | 精品一区二区三区波多野结衣 | 偷窥日本少妇撒尿chinese | 国产精品美女久久久 | 永久黄网站色视频免费直播 | 性色欲网站人妻丰满中文久久不卡 | 风流少妇按摩来高潮 | 高清不卡一区二区三区 | 亚洲欧美日韩国产精品一区二区 | 亚洲日韩乱码中文无码蜜桃臀网站 | 亚洲午夜无码久久 | 亚洲成av人综合在线观看 | 亚洲日韩一区二区三区 | 十八禁真人啪啪免费网站 | 午夜精品一区二区三区在线观看 | 精品午夜福利在线观看 | 成人精品视频一区二区三区尤物 | 欧美性生交xxxxx久久久 | 亚无码乱人伦一区二区 | 日日麻批免费40分钟无码 | 在线精品亚洲一区二区 | 成人三级无码视频在线观看 | 国产精品高潮呻吟av久久4虎 | 日产国产精品亚洲系列 | 精品aⅴ一区二区三区 | 国产特级毛片aaaaaaa高清 | 国产福利视频一区二区 | 夜夜躁日日躁狠狠久久av | 四虎影视成人永久免费观看视频 | 欧美熟妇另类久久久久久多毛 | 未满成年国产在线观看 | 高潮毛片无遮挡高清免费视频 | 国产精品成人av在线观看 | 免费视频欧美无人区码 | 欧美自拍另类欧美综合图片区 | 国产精品va在线观看无码 | 免费网站看v片在线18禁无码 | 乱码午夜-极国产极内射 | 性欧美牲交在线视频 | 99久久99久久免费精品蜜桃 | 18无码粉嫩小泬无套在线观看 | 在线成人www免费观看视频 | 偷窥村妇洗澡毛毛多 | 99精品国产综合久久久久五月天 | 免费看少妇作爱视频 | 亚洲爆乳大丰满无码专区 | 亚洲精品欧美二区三区中文字幕 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲综合色区中文字幕 | 蜜桃av抽搐高潮一区二区 | 少妇人妻偷人精品无码视频 | 97se亚洲精品一区 | 欧美人与禽猛交狂配 | 99久久99久久免费精品蜜桃 | 东京热一精品无码av | 人人超人人超碰超国产 | 国产人妻大战黑人第1集 | 最近免费中文字幕中文高清百度 | 中文字幕av日韩精品一区二区 | 久久久中文字幕日本无吗 | 天天摸天天透天天添 | 亚洲国产欧美国产综合一区 | 一本无码人妻在中文字幕免费 | 99国产欧美久久久精品 | 国产麻豆精品一区二区三区v视界 | 黑人大群体交免费视频 | 中文字幕av伊人av无码av | 国内揄拍国内精品少妇国语 | 国产精品国产三级国产专播 | 国产精品毛片一区二区 | 亚洲精品久久久久久一区二区 | 欧美大屁股xxxxhd黑色 | 色狠狠av一区二区三区 | 国产精品无码成人午夜电影 | 欧美xxxx黑人又粗又长 | 偷窥日本少妇撒尿chinese | 国产人妻人伦精品1国产丝袜 | 欧美变态另类xxxx | 亚洲欧美精品aaaaaa片 | 亚洲春色在线视频 | 一本精品99久久精品77 | 我要看www免费看插插视频 | 97色伦图片97综合影院 | 黑人巨大精品欧美一区二区 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 久久亚洲精品中文字幕无男同 | 久久伊人色av天堂九九小黄鸭 | 久久久久99精品国产片 | 秋霞成人午夜鲁丝一区二区三区 | 无码人妻丰满熟妇区五十路百度 | 久久久av男人的天堂 | 成人精品天堂一区二区三区 | 99久久99久久免费精品蜜桃 | 性色欲网站人妻丰满中文久久不卡 | 俺去俺来也www色官网 | 丝袜人妻一区二区三区 | 最新国产麻豆aⅴ精品无码 | 欧美亚洲日韩国产人成在线播放 | 男女爱爱好爽视频免费看 | 丰满岳乱妇在线观看中字无码 | 狠狠色噜噜狠狠狠狠7777米奇 | 精品国产一区二区三区av 性色 | 亚洲精品欧美二区三区中文字幕 | 俺去俺来也在线www色官网 | 特黄特色大片免费播放器图片 | 国产亚洲视频中文字幕97精品 | 亚洲精品欧美二区三区中文字幕 | 久久午夜无码鲁丝片秋霞 | 亚洲国精产品一二二线 | 亚洲成熟女人毛毛耸耸多 | 丰满少妇熟乱xxxxx视频 | 一本色道久久综合亚洲精品不卡 | 亚洲国产精品久久久天堂 | 亚洲色大成网站www国产 | 蜜桃av抽搐高潮一区二区 | 国产一区二区三区日韩精品 | 人人澡人人妻人人爽人人蜜桃 | 亚洲色欲久久久综合网东京热 | 国产精品久久久av久久久 | 大地资源中文第3页 | 高潮喷水的毛片 | 激情内射亚州一区二区三区爱妻 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产女主播喷水视频在线观看 | 国产艳妇av在线观看果冻传媒 | 成人av无码一区二区三区 | 精品夜夜澡人妻无码av蜜桃 | 日本www一道久久久免费榴莲 | 一本久久伊人热热精品中文字幕 | 亚洲精品国产精品乱码视色 | 黑人玩弄人妻中文在线 | 牲欲强的熟妇农村老妇女 | 精品 日韩 国产 欧美 视频 | 日日橹狠狠爱欧美视频 | 欧美精品在线观看 | 国产色视频一区二区三区 | 久久99精品国产麻豆蜜芽 | 欧美日韩视频无码一区二区三 | √8天堂资源地址中文在线 | 好屌草这里只有精品 | 免费国产成人高清在线观看网站 | 欧美日韩一区二区三区自拍 | 国产亚洲精品久久久久久国模美 | 女人被男人躁得好爽免费视频 | 久久国产精品_国产精品 | 成人无码影片精品久久久 | 中文字幕 人妻熟女 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲а∨天堂久久精品2021 | 亚洲精品久久久久久一区二区 | 亚洲a无码综合a国产av中文 | 日本又色又爽又黄的a片18禁 | www国产亚洲精品久久久日本 | 亚洲色无码一区二区三区 | 少妇高潮一区二区三区99 | 初尝人妻少妇中文字幕 | 亚洲精品一区三区三区在线观看 | 人妻少妇精品久久 | 国产综合色产在线精品 | 国产精品久久国产三级国 | 欧美性猛交xxxx富婆 | 性色av无码免费一区二区三区 | 久久午夜无码鲁丝片 | 精品水蜜桃久久久久久久 | 2020最新国产自产精品 | 国产日产欧产精品精品app | 亚洲人亚洲人成电影网站色 | 2020久久香蕉国产线看观看 | 日本丰满护士爆乳xxxx | 久久97精品久久久久久久不卡 | 亚洲色无码一区二区三区 | 久久亚洲中文字幕精品一区 | 婷婷色婷婷开心五月四房播播 | 免费人成在线观看网站 | 亚洲日本va午夜在线电影 | 国产精品无码成人午夜电影 | 午夜免费福利小电影 | 成人无码精品一区二区三区 | 红桃av一区二区三区在线无码av | 人人澡人人妻人人爽人人蜜桃 | 日产精品高潮呻吟av久久 | 亚洲午夜无码久久 | 亚洲精品一区二区三区婷婷月 | 少妇无码av无码专区在线观看 | 性生交大片免费看l | 国産精品久久久久久久 | 亚洲国产成人av在线观看 | 国产性生交xxxxx无码 | 欧美国产日产一区二区 | 亚洲欧美国产精品久久 | 日本精品少妇一区二区三区 | 国内精品久久久久久中文字幕 | 欧美人与禽猛交狂配 | 成人欧美一区二区三区黑人免费 | 久久国产精品精品国产色婷婷 | 免费观看又污又黄的网站 | 中文精品无码中文字幕无码专区 | 国产国语老龄妇女a片 | 色老头在线一区二区三区 | 国内精品人妻无码久久久影院蜜桃 | 国产精品欧美成人 | 国产成人精品必看 | 中文字幕乱码人妻无码久久 | 中文字幕无码人妻少妇免费 | 亚洲综合在线一区二区三区 | 日本成熟视频免费视频 | 伊人久久大香线蕉午夜 | 男人的天堂2018无码 | 亚洲欧美色中文字幕在线 | 午夜理论片yy44880影院 | 色婷婷久久一区二区三区麻豆 | 久久99精品久久久久久 | 日本精品久久久久中文字幕 | 妺妺窝人体色www婷婷 | 亚洲国产成人a精品不卡在线 | 女人色极品影院 | 亚洲精品国产精品乱码不卡 | 亚洲精品国产a久久久久久 | 丰满人妻精品国产99aⅴ | 亚洲成av人综合在线观看 | 97资源共享在线视频 | 日韩精品无码免费一区二区三区 | 精品无码一区二区三区爱欲 | 久久99精品久久久久久 | 久久精品国产99久久6动漫 | 欧美日韩一区二区免费视频 | 男人扒开女人内裤强吻桶进去 | 精品一二三区久久aaa片 | 国产精品久久久久久亚洲毛片 | 国产国语老龄妇女a片 | 成人影院yy111111在线观看 | 少妇性俱乐部纵欲狂欢电影 | 自拍偷自拍亚洲精品10p | 亚洲a无码综合a国产av中文 | 99精品视频在线观看免费 | 亚洲无人区午夜福利码高清完整版 | 国产人妻人伦精品1国产丝袜 | 亚洲精品国产a久久久久久 | 国产在热线精品视频 | 伊人久久大香线蕉av一区二区 | 亚洲国产精品毛片av不卡在线 | 久青草影院在线观看国产 | 久久99精品久久久久婷婷 | 激情国产av做激情国产爱 | 性欧美牲交xxxxx视频 | 欧美熟妇另类久久久久久多毛 | 粗大的内捧猛烈进出视频 | 亚洲精品午夜国产va久久成人 | 99久久久无码国产精品免费 | 青草青草久热国产精品 | 欧美成人高清在线播放 | 日韩人妻无码中文字幕视频 | 无码av免费一区二区三区试看 | 少妇的肉体aa片免费 | 国产真实伦对白全集 | 澳门永久av免费网站 | 亚洲の无码国产の无码步美 | 狠狠躁日日躁夜夜躁2020 | 好男人www社区 | 欧美丰满熟妇xxxx性ppx人交 | 成人精品视频一区二区三区尤物 | 少妇激情av一区二区 | 国产人妻精品一区二区三区不卡 | 亚洲中文字幕无码中文字在线 | 色情久久久av熟女人妻网站 | 国产av剧情md精品麻豆 | 欧美日韩综合一区二区三区 | 国产无遮挡吃胸膜奶免费看 | 亚洲中文字幕va福利 | 水蜜桃av无码 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 欧美老妇与禽交 | 久精品国产欧美亚洲色aⅴ大片 | 国产精品人妻一区二区三区四 | 荫蒂添的好舒服视频囗交 | 亚洲人成网站在线播放942 | 色欲久久久天天天综合网精品 | 国产欧美精品一区二区三区 | 亚洲精品无码人妻无码 | 熟妇女人妻丰满少妇中文字幕 | 国产精品a成v人在线播放 | 亚洲精品综合一区二区三区在线 | 成 人 免费观看网站 | 免费无码肉片在线观看 | 精品国产一区二区三区四区在线看 | 久久zyz资源站无码中文动漫 | 国产精品va在线播放 | 久久国内精品自在自线 | 日韩精品无码一区二区中文字幕 | 免费视频欧美无人区码 | 中文字幕av日韩精品一区二区 | 牲欲强的熟妇农村老妇女视频 | 扒开双腿疯狂进出爽爽爽视频 | 窝窝午夜理论片影院 | 影音先锋中文字幕无码 | 国产激情一区二区三区 | 18无码粉嫩小泬无套在线观看 | 日韩欧美中文字幕公布 | 国产精品理论片在线观看 | 国产在线无码精品电影网 | 日本欧美一区二区三区乱码 | 少妇愉情理伦片bd | 亚洲成av人在线观看网址 | 久久国内精品自在自线 | 国产人成高清在线视频99最全资源 | 精品无码一区二区三区爱欲 | 67194成是人免费无码 | 日韩欧美中文字幕在线三区 | 亚洲精品成人福利网站 | 欧美人与禽猛交狂配 | 精品国产一区二区三区四区在线看 | 熟妇人妻无码xxx视频 | av无码久久久久不卡免费网站 | 图片区 小说区 区 亚洲五月 | 乱人伦中文视频在线观看 | 亚洲成av人在线观看网址 | 99re在线播放 | 东京热男人av天堂 | 青青草原综合久久大伊人精品 | 久久午夜夜伦鲁鲁片无码免费 | 亚洲 欧美 激情 小说 另类 | 又湿又紧又大又爽a视频国产 | 中文毛片无遮挡高清免费 | 成人一在线视频日韩国产 | 无码国内精品人妻少妇 | 亚洲国产精品久久久久久 | 强开小婷嫩苞又嫩又紧视频 | yw尤物av无码国产在线观看 | 成年美女黄网站色大免费视频 | 巨爆乳无码视频在线观看 | av无码不卡在线观看免费 | 无码人妻出轨黑人中文字幕 | 97夜夜澡人人双人人人喊 | 中文字幕乱妇无码av在线 | 中文亚洲成a人片在线观看 | 国产精品国产三级国产专播 | 狠狠色欧美亚洲狠狠色www | 乱人伦中文视频在线观看 | 在线观看国产一区二区三区 | 亚洲a无码综合a国产av中文 | 精品成人av一区二区三区 | 成人免费无码大片a毛片 | 久久久久成人精品免费播放动漫 | 国精产品一品二品国精品69xx | 无码福利日韩神码福利片 | 国产午夜精品一区二区三区嫩草 | 亚洲成色在线综合网站 | 亚洲中文字幕乱码av波多ji | 国产精品久久久久无码av色戒 | 中文无码伦av中文字幕 | 性史性农村dvd毛片 | 九九在线中文字幕无码 | 色情久久久av熟女人妻网站 | 97久久精品无码一区二区 | 麻豆果冻传媒2021精品传媒一区下载 | 秋霞成人午夜鲁丝一区二区三区 | 色欲综合久久中文字幕网 | 国产激情精品一区二区三区 | 久久国内精品自在自线 | 天堂一区人妻无码 | 国产熟妇另类久久久久 | 无码人妻丰满熟妇区五十路百度 | 亚洲阿v天堂在线 | 亚洲伊人久久精品影院 | 亚洲阿v天堂在线 | 少妇高潮喷潮久久久影院 | 成人精品视频一区二区三区尤物 | 99精品国产综合久久久久五月天 | 午夜无码区在线观看 | 国产乱码精品一品二品 | 成人动漫在线观看 | 强伦人妻一区二区三区视频18 | 国产va免费精品观看 | 又粗又大又硬毛片免费看 | 人妻无码久久精品人妻 | 国产精品美女久久久久av爽李琼 | 亚洲色成人中文字幕网站 | 国产精品.xx视频.xxtv | 性生交大片免费看l | 国产精品无码mv在线观看 | 在线а√天堂中文官网 | 九九久久精品国产免费看小说 | 呦交小u女精品视频 | 成年美女黄网站色大免费全看 | a片免费视频在线观看 | 久久99国产综合精品 | 久久久久久av无码免费看大片 | 亚洲自偷精品视频自拍 | 国内精品久久毛片一区二区 | 图片区 小说区 区 亚洲五月 | 国产激情艳情在线看视频 | 蜜臀av无码人妻精品 | 1000部啪啪未满十八勿入下载 | 波多野结衣乳巨码无在线观看 | 国产亚洲人成在线播放 | 久久国产自偷自偷免费一区调 | 又大又黄又粗又爽的免费视频 | 免费看男女做好爽好硬视频 | 最近免费中文字幕中文高清百度 | 亚洲春色在线视频 | 欧洲熟妇色 欧美 | 香蕉久久久久久av成人 | 国产熟妇高潮叫床视频播放 | 色欲综合久久中文字幕网 | 欧美日韩视频无码一区二区三 | 国内揄拍国内精品少妇国语 | 18禁止看的免费污网站 | 国产午夜福利亚洲第一 | 精品一区二区三区波多野结衣 | 亚洲国产成人a精品不卡在线 | 强伦人妻一区二区三区视频18 | 久久无码专区国产精品s | 亚洲国产欧美日韩精品一区二区三区 | 丰满人妻被黑人猛烈进入 | 色综合视频一区二区三区 | 99久久精品日本一区二区免费 | 亚洲欧洲无卡二区视頻 | 在线天堂新版最新版在线8 | 国产艳妇av在线观看果冻传媒 | 亚洲啪av永久无码精品放毛片 | 无码人妻黑人中文字幕 | 精品无码国产自产拍在线观看蜜 | 免费中文字幕日韩欧美 | 99久久精品日本一区二区免费 | 日本一区二区更新不卡 | 狠狠色欧美亚洲狠狠色www | 国产成人无码一二三区视频 | 亚洲国产精品成人久久蜜臀 | 欧美性黑人极品hd | 亚拍精品一区二区三区探花 | 国产精品igao视频网 | 天堂а√在线地址中文在线 | 日本熟妇乱子伦xxxx | 在线播放免费人成毛片乱码 | 久久久久亚洲精品中文字幕 | 日本精品少妇一区二区三区 | 亚洲精品午夜国产va久久成人 | 牲交欧美兽交欧美 | 国产av一区二区精品久久凹凸 | 精品少妇爆乳无码av无码专区 | 人人澡人人妻人人爽人人蜜桃 | 精品国产精品久久一区免费式 | av小次郎收藏 | 老太婆性杂交欧美肥老太 | 成人免费视频一区二区 | 丰满妇女强制高潮18xxxx | 国产精品资源一区二区 | 色老头在线一区二区三区 | 天堂亚洲免费视频 | 在线 国产 欧美 亚洲 天堂 | 国产亚洲tv在线观看 | 亚洲一区二区三区 | 97se亚洲精品一区 | 午夜丰满少妇性开放视频 | 国产成人精品优优av | 精品偷自拍另类在线观看 | 国产人妻精品午夜福利免费 | 久久人人爽人人人人片 | 又湿又紧又大又爽a视频国产 | 精品久久综合1区2区3区激情 | 久久亚洲精品成人无码 | 亚洲色大成网站www | 理论片87福利理论电影 | 亚洲综合伊人久久大杳蕉 | 亚无码乱人伦一区二区 | 亚洲国产成人av在线观看 | 亚洲精品综合一区二区三区在线 | 国产亚洲精品久久久久久 | 亚洲自偷精品视频自拍 | 99精品视频在线观看免费 | 中文精品无码中文字幕无码专区 | 高中生自慰www网站 | 日本又色又爽又黄的a片18禁 | 亚洲精品中文字幕久久久久 | 特级做a爰片毛片免费69 | 亚洲国产欧美在线成人 | 色婷婷综合激情综在线播放 | 日韩精品a片一区二区三区妖精 | 久久亚洲日韩精品一区二区三区 | 日本一区二区三区免费高清 | 国产人妻精品午夜福利免费 | 极品嫩模高潮叫床 | 日韩精品乱码av一区二区 | 中文字幕久久久久人妻 | 亚洲午夜无码久久 | 欧美性猛交xxxx富婆 | 蜜桃视频插满18在线观看 | 欧洲极品少妇 | 国产精品人人妻人人爽 | 亚洲成熟女人毛毛耸耸多 | 精品国产国产综合精品 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲精品久久久久中文第一幕 | 日本精品久久久久中文字幕 | 日韩av无码中文无码电影 | 国产在线一区二区三区四区五区 | 久久人人爽人人人人片 | 人人妻人人澡人人爽欧美一区 | 欧美xxxx黑人又粗又长 | 亚洲中文字幕成人无码 | 青青青爽视频在线观看 | 人妻少妇精品无码专区动漫 | 国产偷抇久久精品a片69 | 狠狠亚洲超碰狼人久久 | 少妇被黑人到高潮喷出白浆 | 高潮毛片无遮挡高清免费 | 久久人人爽人人爽人人片ⅴ | 亚洲 高清 成人 动漫 | 国产明星裸体无码xxxx视频 | 国产成人精品一区二区在线小狼 | 人人妻人人澡人人爽欧美精品 | 亚洲精品一区二区三区在线 | 欧美丰满老熟妇xxxxx性 | 国产精品沙发午睡系列 | 无遮挡国产高潮视频免费观看 | 国内少妇偷人精品视频 | 亚洲一区二区三区含羞草 | 国产亚洲欧美日韩亚洲中文色 | 99久久婷婷国产综合精品青草免费 | 熟妇人妻激情偷爽文 | www国产精品内射老师 | 帮老师解开蕾丝奶罩吸乳网站 | 思思久久99热只有频精品66 | 亚洲精品午夜国产va久久成人 | 波多野结衣一区二区三区av免费 | 377p欧洲日本亚洲大胆 | 欧洲精品码一区二区三区免费看 | 久久天天躁夜夜躁狠狠 | 欧美xxxx黑人又粗又长 | 国产乱人无码伦av在线a | 一本久久a久久精品亚洲 | 两性色午夜免费视频 | 无码av最新清无码专区吞精 | 日日橹狠狠爱欧美视频 | 丰满肥臀大屁股熟妇激情视频 | 四虎影视成人永久免费观看视频 | 亚洲一区av无码专区在线观看 | 天天躁日日躁狠狠躁免费麻豆 | 中文字幕无码热在线视频 | 午夜时刻免费入口 | 国产精品久久国产精品99 | 天天躁日日躁狠狠躁免费麻豆 | 高中生自慰www网站 | 黑人巨大精品欧美黑寡妇 | 少女韩国电视剧在线观看完整 | 国产一区二区不卡老阿姨 | 国产综合色产在线精品 | 性啪啪chinese东北女人 | 亚洲熟妇色xxxxx亚洲 | 久久无码人妻影院 | 国产97人人超碰caoprom | 老司机亚洲精品影院无码 | 在线观看国产午夜福利片 | 国产精品久久久一区二区三区 | 丰满少妇人妻久久久久久 | 国产香蕉尹人综合在线观看 | 激情综合激情五月俺也去 | 亚洲中文字幕无码一久久区 | 狠狠噜狠狠狠狠丁香五月 | 亚洲精品无码国产 | 免费观看又污又黄的网站 | 国产又爽又猛又粗的视频a片 | 男人的天堂2018无码 | 激情内射亚州一区二区三区爱妻 | 欧美日韩人成综合在线播放 | 午夜丰满少妇性开放视频 | 亚洲精品成人福利网站 | 思思久久99热只有频精品66 | 日本成熟视频免费视频 | 欧美熟妇另类久久久久久不卡 | 性史性农村dvd毛片 | 图片区 小说区 区 亚洲五月 | 人人妻人人澡人人爽精品欧美 | 全黄性性激高免费视频 | 久久亚洲日韩精品一区二区三区 | www国产亚洲精品久久网站 | 亚洲a无码综合a国产av中文 | 亚洲精品久久久久久一区二区 | 久久亚洲中文字幕无码 | 亚洲成a人片在线观看日本 | 久久午夜无码鲁丝片 | 狠狠cao日日穞夜夜穞av | 亚洲欧洲中文日韩av乱码 | 中文字幕+乱码+中文字幕一区 | 亚洲色大成网站www国产 | 国产精品久久久久9999小说 | 秋霞成人午夜鲁丝一区二区三区 | 免费无码一区二区三区蜜桃大 | 中国女人内谢69xxxxxa片 | 国产成人无码av在线影院 | 蜜桃视频插满18在线观看 | 99久久久无码国产aaa精品 | 精品人妻人人做人人爽夜夜爽 | 亚洲精品一区二区三区婷婷月 | 又粗又大又硬毛片免费看 | 欧美性猛交内射兽交老熟妇 | 国产激情综合五月久久 | 国产激情无码一区二区 | 久久久亚洲欧洲日产国码αv | 久久 国产 尿 小便 嘘嘘 | 亚洲日本va午夜在线电影 | 国产成人av免费观看 | 男女超爽视频免费播放 | 国产无遮挡吃胸膜奶免费看 | 久久久久国色av免费观看性色 | 国产做国产爱免费视频 | 精品国产青草久久久久福利 | 熟妇人妻无码xxx视频 | 久久精品成人欧美大片 | 大肉大捧一进一出视频出来呀 | 一区二区三区高清视频一 | 国产性生交xxxxx无码 | 亚洲国产综合无码一区 | 内射爽无广熟女亚洲 | 又色又爽又黄的美女裸体网站 | 久久www免费人成人片 | 精品国产精品久久一区免费式 | 人妻少妇被猛烈进入中文字幕 | 亚洲综合久久一区二区 | 欧美日韩一区二区综合 | 永久免费精品精品永久-夜色 | 国产手机在线αⅴ片无码观看 | 精品 日韩 国产 欧美 视频 | 无码人妻出轨黑人中文字幕 | 人妻与老人中文字幕 | 东京热一精品无码av | 中文字幕乱码人妻二区三区 | 成年美女黄网站色大免费视频 | 中文字幕无码免费久久9一区9 | 日本精品高清一区二区 | 亚洲中文字幕久久无码 | 久久亚洲精品成人无码 | 蜜桃无码一区二区三区 | 国产色视频一区二区三区 | 国产精品对白交换视频 | 国产亚洲精品久久久久久久 | 国产av无码专区亚洲a∨毛片 | 国产手机在线αⅴ片无码观看 | 精品久久久久久亚洲精品 | 亚洲日韩av一区二区三区四区 | 欧美阿v高清资源不卡在线播放 | 高中生自慰www网站 | 人人爽人人爽人人片av亚洲 | 麻豆国产人妻欲求不满 | 午夜免费福利小电影 | 亚洲无人区午夜福利码高清完整版 | 日本一本二本三区免费 | 亚洲狠狠婷婷综合久久 | 人人妻人人澡人人爽人人精品浪潮 | 强辱丰满人妻hd中文字幕 | 国内精品人妻无码久久久影院 | 国产精品美女久久久久av爽李琼 | 亚洲天堂2017无码 | 男人的天堂2018无码 | 国产疯狂伦交大片 | 欧美人与禽zoz0性伦交 | 亚洲爆乳精品无码一区二区三区 | 2019午夜福利不卡片在线 | 天堂а√在线中文在线 | 日韩欧美群交p片內射中文 | aⅴ亚洲 日韩 色 图网站 播放 | 熟妇人妻激情偷爽文 | 亚洲精品中文字幕乱码 | 亚洲综合无码一区二区三区 | 国产日产欧产精品精品app | 久久国产精品萌白酱免费 | 亚洲成av人在线观看网址 | 人人妻人人藻人人爽欧美一区 | 女人高潮内射99精品 | 少妇邻居内射在线 | 人人妻人人藻人人爽欧美一区 | 少妇性荡欲午夜性开放视频剧场 | 国产69精品久久久久app下载 | 国内少妇偷人精品视频 | 国产片av国语在线观看 | 亚无码乱人伦一区二区 | 国产内射爽爽大片视频社区在线 | 一个人免费观看的www视频 | 人妻无码αv中文字幕久久琪琪布 | 亚洲精品成人av在线 | 天海翼激烈高潮到腰振不止 | 乱人伦人妻中文字幕无码久久网 | 少妇太爽了在线观看 | 成人免费视频一区二区 | 国产乱人伦偷精品视频 | 领导边摸边吃奶边做爽在线观看 | 国产成人精品一区二区在线小狼 | 国产明星裸体无码xxxx视频 | 亚洲乱亚洲乱妇50p | 亚洲 日韩 欧美 成人 在线观看 | 99久久久国产精品无码免费 | 日韩av激情在线观看 | 亚洲熟女一区二区三区 | 亚洲日韩精品欧美一区二区 | 国产精品无码一区二区三区不卡 | 波多野结衣av在线观看 | 国产69精品久久久久app下载 | 免费中文字幕日韩欧美 | 97精品人妻一区二区三区香蕉 | 精品厕所偷拍各类美女tp嘘嘘 | 无码av中文字幕免费放 | 久久97精品久久久久久久不卡 | 精品欧美一区二区三区久久久 | 丰满肥臀大屁股熟妇激情视频 | 东京无码熟妇人妻av在线网址 | 中文字幕人妻无码一区二区三区 | 久久熟妇人妻午夜寂寞影院 | 狠狠色噜噜狠狠狠7777奇米 | 内射老妇bbwx0c0ck | 精品久久久久久亚洲精品 | 中文精品久久久久人妻不卡 | 色 综合 欧美 亚洲 国产 | 丰满人妻翻云覆雨呻吟视频 | 牛和人交xxxx欧美 | 亚洲色大成网站www | 国产激情无码一区二区 | 国产在热线精品视频 | 欧美老妇交乱视频在线观看 | 无码午夜成人1000部免费视频 | 国产亚洲人成在线播放 | 欧美性生交xxxxx久久久 | 久久久www成人免费毛片 | 午夜熟女插插xx免费视频 | 精品无码国产自产拍在线观看蜜 | 无码毛片视频一区二区本码 | 亚洲啪av永久无码精品放毛片 | 天天综合网天天综合色 | 亚洲日韩av一区二区三区四区 | 中文字幕av无码一区二区三区电影 | 久久精品中文字幕大胸 | 国产精品怡红院永久免费 | 亚洲精品一区二区三区婷婷月 | 精品 日韩 国产 欧美 视频 | 国产特级毛片aaaaaa高潮流水 | 日韩视频 中文字幕 视频一区 | 国产成人精品一区二区在线小狼 | 国产肉丝袜在线观看 | 亚洲熟妇色xxxxx亚洲 | 国产欧美熟妇另类久久久 | 欧美性猛交xxxx富婆 | 成年美女黄网站色大免费视频 | 无码帝国www无码专区色综合 | 人妻无码久久精品人妻 | 美女毛片一区二区三区四区 | 又紧又大又爽精品一区二区 | a国产一区二区免费入口 | 久久久久国色av免费观看性色 | 动漫av一区二区在线观看 | 久久天天躁夜夜躁狠狠 | 亚洲日韩av一区二区三区中文 | 免费网站看v片在线18禁无码 | 人妻与老人中文字幕 | 少妇高潮一区二区三区99 | 亚洲精品一区二区三区大桥未久 | 亚欧洲精品在线视频免费观看 | 国产又爽又猛又粗的视频a片 | 亚洲s色大片在线观看 | 377p欧洲日本亚洲大胆 | 乌克兰少妇xxxx做受 | 亚洲精品中文字幕久久久久 | 波多野结衣高清一区二区三区 | 国产人妻人伦精品1国产丝袜 | 国产一区二区三区精品视频 | 亚洲精品国产a久久久久久 | 国产特级毛片aaaaaaa高清 | 亚洲色无码一区二区三区 | 99国产精品白浆在线观看免费 | 国产午夜精品一区二区三区嫩草 | 欧美国产日产一区二区 | 精品国产国产综合精品 | 精品人妻av区 | 无码人妻丰满熟妇区毛片18 | а√天堂www在线天堂小说 | 久久精品人人做人人综合 | 国语自产偷拍精品视频偷 | 色窝窝无码一区二区三区色欲 | 无码国内精品人妻少妇 | 99久久精品无码一区二区毛片 | 水蜜桃亚洲一二三四在线 | 给我免费的视频在线观看 | 天下第一社区视频www日本 | 欧美zoozzooz性欧美 | 精品人妻人人做人人爽夜夜爽 | 精品国产成人一区二区三区 | 欧美放荡的少妇 | 久久婷婷五月综合色国产香蕉 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 日本va欧美va欧美va精品 | 国产午夜无码视频在线观看 | 亚洲熟妇色xxxxx欧美老妇y | 国产成人精品一区二区在线小狼 | 亚洲人成人无码网www国产 | 欧美精品无码一区二区三区 | 国产艳妇av在线观看果冻传媒 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产成人精品一区二区在线小狼 | 国产人妖乱国产精品人妖 | 纯爱无遮挡h肉动漫在线播放 | 中文毛片无遮挡高清免费 | 丰满岳乱妇在线观看中字无码 | 中文字幕av日韩精品一区二区 | 国产欧美精品一区二区三区 | 人人妻人人澡人人爽人人精品 | 麻豆md0077饥渴少妇 | 人妻aⅴ无码一区二区三区 | 精品国产乱码久久久久乱码 | 中文字幕无码人妻少妇免费 | 日本精品人妻无码免费大全 | 国产无遮挡又黄又爽又色 | 久久人人爽人人人人片 | 久久久久久久人妻无码中文字幕爆 | 中文字幕精品av一区二区五区 | 亚洲欧洲日本综合aⅴ在线 | 激情爆乳一区二区三区 | 久久99精品久久久久久动态图 | 一本精品99久久精品77 | 国产精品香蕉在线观看 | 无码人妻av免费一区二区三区 | 亚洲日本va中文字幕 | 国产精品国产三级国产专播 | 人妻少妇精品无码专区二区 | 99麻豆久久久国产精品免费 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 午夜精品一区二区三区的区别 | 国产婷婷色一区二区三区在线 | 日日碰狠狠躁久久躁蜜桃 | 欧美freesex黑人又粗又大 | 国产精品无套呻吟在线 | www国产亚洲精品久久网站 | 国产精品毛片一区二区 | 亚洲综合伊人久久大杳蕉 | 国产性生大片免费观看性 | 乌克兰少妇性做爰 | 乌克兰少妇xxxx做受 | 任你躁国产自任一区二区三区 | 国产午夜亚洲精品不卡下载 | 国产综合色产在线精品 | 国产人妻人伦精品 | 国内精品久久毛片一区二区 | 国产激情一区二区三区 | 午夜不卡av免费 一本久久a久久精品vr综合 | 欧美激情一区二区三区成人 | 日日躁夜夜躁狠狠躁 | 55夜色66夜色国产精品视频 | 免费国产黄网站在线观看 | 成人亚洲精品久久久久软件 | 国产精品丝袜黑色高跟鞋 | 无遮无挡爽爽免费视频 | 男女作爱免费网站 | 色五月五月丁香亚洲综合网 | 性色欲情网站iwww九文堂 | 国产精品鲁鲁鲁 | 国产两女互慰高潮视频在线观看 | 亚洲精品久久久久中文第一幕 | 日本肉体xxxx裸交 | 国产做国产爱免费视频 | 永久免费观看国产裸体美女 | 久久久精品456亚洲影院 | 欧美日韩一区二区综合 | 俺去俺来也www色官网 | 日本成熟视频免费视频 | 国产手机在线αⅴ片无码观看 | 四虎永久在线精品免费网址 | 丰腴饱满的极品熟妇 | 久久久久亚洲精品男人的天堂 | 亚洲天堂2017无码 | 国产色在线 | 国产 | 天天燥日日燥 | 中文字幕日韩精品一区二区三区 | 无码吃奶揉捏奶头高潮视频 | 国产成人无码av在线影院 | 久久久www成人免费毛片 | 亚洲国产精品一区二区第一页 | 人妻插b视频一区二区三区 | 激情人妻另类人妻伦 | 久久综合狠狠综合久久综合88 | 色欲综合久久中文字幕网 | 日本在线高清不卡免费播放 | 国产激情艳情在线看视频 | 少妇高潮一区二区三区99 | 久久人人爽人人爽人人片ⅴ | 国产精品无码一区二区三区不卡 | 中文无码精品a∨在线观看不卡 | 久久久久久九九精品久 | 日韩欧美中文字幕在线三区 | 精品国产国产综合精品 | 国产精品久久福利网站 | 国产精品久久久久久亚洲毛片 | 欧美日韩一区二区免费视频 | 蜜桃臀无码内射一区二区三区 | 日韩人妻无码中文字幕视频 | 日韩欧美群交p片內射中文 | 日本高清一区免费中文视频 | 日日摸夜夜摸狠狠摸婷婷 | 欧美人与禽zoz0性伦交 | 四虎4hu永久免费 | 狂野欧美激情性xxxx | 麻花豆传媒剧国产免费mv在线 | 亚洲熟妇自偷自拍另类 | 午夜成人1000部免费视频 | 日本一区二区三区免费高清 | 日本又色又爽又黄的a片18禁 | 性做久久久久久久免费看 | 国产精品久久久久久久9999 | 波多野结衣av一区二区全免费观看 | 婷婷色婷婷开心五月四房播播 | 亚洲综合无码一区二区三区 | 国产精品自产拍在线观看 | 精品 日韩 国产 欧美 视频 | 亚洲gv猛男gv无码男同 | 久久久无码中文字幕久... | 人人超人人超碰超国产 | 久久国语露脸国产精品电影 | 欧美性生交活xxxxxdddd | 久久精品国产一区二区三区肥胖 | 精品aⅴ一区二区三区 | 女人高潮内射99精品 | 精品久久8x国产免费观看 | 沈阳熟女露脸对白视频 | 亚洲国产精品久久久久久 | av人摸人人人澡人人超碰下载 | 日产精品高潮呻吟av久久 | 久久久精品人妻久久影视 | 精品人妻中文字幕有码在线 | 国产香蕉97碰碰久久人人 | 少妇性l交大片欧洲热妇乱xxx | 人妻有码中文字幕在线 | 国精产品一品二品国精品69xx | 亚洲の无码国产の无码步美 | 女人和拘做爰正片视频 | 久久99热只有频精品8 | 午夜性刺激在线视频免费 | yw尤物av无码国产在线观看 | 国精产品一区二区三区 | 欧美黑人性暴力猛交喷水 | 精品国偷自产在线 | 欧美国产日韩亚洲中文 | 亚洲中文字幕av在天堂 | 欧美国产日韩亚洲中文 | 亚洲区小说区激情区图片区 | 色情久久久av熟女人妻网站 | 久久精品人妻少妇一区二区三区 | 妺妺窝人体色www婷婷 | 性欧美疯狂xxxxbbbb | 国产一区二区三区影院 | 国模大胆一区二区三区 | 成人欧美一区二区三区黑人 | 国产肉丝袜在线观看 | 日日碰狠狠丁香久燥 | 欧美精品无码一区二区三区 | 国色天香社区在线视频 | 天干天干啦夜天干天2017 | 亚洲精品久久久久中文第一幕 | 亚洲精品国产精品乱码视色 | 亚洲成av人综合在线观看 | 欧美人与禽猛交狂配 | 日韩 欧美 动漫 国产 制服 | 成人试看120秒体验区 | 日韩亚洲欧美精品综合 | 亚洲日韩av一区二区三区中文 | 欧洲熟妇精品视频 | 白嫩日本少妇做爰 | 男人的天堂2018无码 | 波多野结衣乳巨码无在线观看 | 欧美性生交活xxxxxdddd | 99久久亚洲精品无码毛片 | 无码av岛国片在线播放 | 3d动漫精品啪啪一区二区中 | 99久久精品无码一区二区毛片 | 国产一精品一av一免费 | 日本xxxx色视频在线观看免费 | 久久国产精品萌白酱免费 | 亚洲人成网站在线播放942 | 日产精品高潮呻吟av久久 | 大地资源网第二页免费观看 | 国产亚洲人成a在线v网站 | 夜夜高潮次次欢爽av女 | 清纯唯美经典一区二区 | 国产超级va在线观看视频 | 国产精品无码一区二区桃花视频 | 给我免费的视频在线观看 | 熟女体下毛毛黑森林 | 在线观看国产一区二区三区 | 性生交片免费无码看人 | 亚洲第一网站男人都懂 | 纯爱无遮挡h肉动漫在线播放 | 精品无码国产一区二区三区av | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 国产无遮挡又黄又爽免费视频 | 久久无码人妻影院 | 欧美色就是色 | 亚洲天堂2017无码中文 | 久久综合给久久狠狠97色 | 精品久久久久久人妻无码中文字幕 | 亚洲一区二区三区无码久久 | 久久久久久久久888 | 欧美一区二区三区 | 国产无套粉嫩白浆在线 | 无码精品人妻一区二区三区av | 风流少妇按摩来高潮 | 国产精品毛片一区二区 | 伊人久久婷婷五月综合97色 | 好爽又高潮了毛片免费下载 | 国产极品美女高潮无套在线观看 | 色综合久久久久综合一本到桃花网 | 色诱久久久久综合网ywww | 一本色道久久综合狠狠躁 | 国产肉丝袜在线观看 | 亚洲男女内射在线播放 | 久久久中文久久久无码 | а√资源新版在线天堂 | 暴力强奷在线播放无码 | 天天摸天天碰天天添 | 亚洲熟妇色xxxxx欧美老妇y | 天天躁夜夜躁狠狠是什么心态 | аⅴ资源天堂资源库在线 | 久久久久久a亚洲欧洲av冫 | 色婷婷av一区二区三区之红樱桃 | 亚洲一区二区三区在线观看网站 | 亚洲爆乳大丰满无码专区 | 久久精品国产一区二区三区肥胖 | 无码帝国www无码专区色综合 | 亚洲精品一区二区三区四区五区 | 十八禁真人啪啪免费网站 | 一本色道久久综合亚洲精品不卡 | 西西人体www44rt大胆高清 | 国产麻豆精品精东影业av网站 | а√天堂www在线天堂小说 | 国产精品国产自线拍免费软件 | 一本久久伊人热热精品中文字幕 | 亚洲а∨天堂久久精品2021 | 两性色午夜免费视频 | aⅴ亚洲 日韩 色 图网站 播放 | 国内精品人妻无码久久久影院 | 粗大的内捧猛烈进出视频 | 欧美日本精品一区二区三区 | 欧美人与物videos另类 | 无码免费一区二区三区 | 3d动漫精品啪啪一区二区中 | 永久黄网站色视频免费直播 | 亚洲国产精品一区二区美利坚 | 高清不卡一区二区三区 | 欧美一区二区三区 | 亚洲天堂2017无码 | 极品尤物被啪到呻吟喷水 | 2019午夜福利不卡片在线 | 国产尤物精品视频 | 国产超级va在线观看视频 | 久久精品人妻少妇一区二区三区 | 麻豆国产丝袜白领秘书在线观看 | 性欧美大战久久久久久久 | 永久免费观看国产裸体美女 | 国产在线一区二区三区四区五区 | 人妻少妇精品无码专区动漫 | 亚洲熟妇色xxxxx欧美老妇 | 精品国产麻豆免费人成网站 | 性欧美牲交xxxxx视频 | 色欲av亚洲一区无码少妇 | 亚洲中文字幕无码中字 | 亚洲综合无码久久精品综合 | 国产精品久久国产三级国 | 国产真实伦对白全集 | 国产乱码精品一品二品 | 少妇性荡欲午夜性开放视频剧场 | 国产av一区二区三区最新精品 | 亚洲呦女专区 | 樱花草在线社区www | 久久综合九色综合97网 | 永久免费精品精品永久-夜色 | 中文字幕人妻无码一夲道 | 国产97人人超碰caoprom | 国产精品igao视频网 | 中文字幕无码热在线视频 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 领导边摸边吃奶边做爽在线观看 | 少妇的肉体aa片免费 | 欧美真人作爱免费视频 | 久久国产自偷自偷免费一区调 | 性做久久久久久久久 | 国产精品无码mv在线观看 | 国产av人人夜夜澡人人爽麻豆 | 国产精品无套呻吟在线 | 久久婷婷五月综合色国产香蕉 | 欧美精品免费观看二区 | 色 综合 欧美 亚洲 国产 | 国产成人精品必看 | 99久久精品国产一区二区蜜芽 | 国内精品久久久久久中文字幕 | 成人无码精品一区二区三区 | 成人aaa片一区国产精品 | 精品日本一区二区三区在线观看 | www成人国产高清内射 | 国产精品对白交换视频 | 在线天堂新版最新版在线8 | 伊人色综合久久天天小片 | 无码播放一区二区三区 | аⅴ资源天堂资源库在线 | 日韩av无码中文无码电影 | 国产午夜精品一区二区三区嫩草 | 亚洲乱亚洲乱妇50p | 精品久久久久久亚洲精品 | 久久午夜无码鲁丝片秋霞 | √8天堂资源地址中文在线 | 亚洲国产日韩a在线播放 | 人妻少妇精品无码专区二区 | 欧美老妇交乱视频在线观看 | 久久99精品国产.久久久久 | 国模大胆一区二区三区 | 无码免费一区二区三区 | 久久亚洲精品中文字幕无男同 | 久久精品国产大片免费观看 | 国产明星裸体无码xxxx视频 | 亚洲一区av无码专区在线观看 | а√天堂www在线天堂小说 | 国产综合色产在线精品 | 免费视频欧美无人区码 | 女高中生第一次破苞av | 亚洲成在人网站无码天堂 | 久久99精品国产麻豆 | 久久亚洲日韩精品一区二区三区 | 国产成人精品久久亚洲高清不卡 | 国产精品亚洲专区无码不卡 | 色老头在线一区二区三区 | 妺妺窝人体色www在线小说 | 荫蒂被男人添的好舒服爽免费视频 | 国产在线精品一区二区高清不卡 | 大地资源网第二页免费观看 | 伊人色综合久久天天小片 | 色狠狠av一区二区三区 | 内射白嫩少妇超碰 | 国产小呦泬泬99精品 | 久久久久成人片免费观看蜜芽 | 亚洲aⅴ无码成人网站国产app | 久久久久久亚洲精品a片成人 | 成人免费视频一区二区 | 99riav国产精品视频 | 久久精品国产亚洲精品 | 人人爽人人爽人人片av亚洲 | 大地资源中文第3页 | 午夜不卡av免费 一本久久a久久精品vr综合 | 日本又色又爽又黄的a片18禁 | 中文精品久久久久人妻不卡 | 在线天堂新版最新版在线8 | √天堂资源地址中文在线 | 强辱丰满人妻hd中文字幕 | 无码成人精品区在线观看 | 国产激情艳情在线看视频 | 乌克兰少妇性做爰 | 玩弄人妻少妇500系列视频 | 人妻少妇被猛烈进入中文字幕 | 亚洲天堂2017无码中文 | 欧美兽交xxxx×视频 | 成人精品天堂一区二区三区 | 东京无码熟妇人妻av在线网址 | 亚洲国产一区二区三区在线观看 | 无码乱肉视频免费大全合集 | 无码人妻出轨黑人中文字幕 | 蜜桃av抽搐高潮一区二区 | 男女爱爱好爽视频免费看 | 国产一区二区三区影院 | 伊人色综合久久天天小片 | 国产亚洲欧美日韩亚洲中文色 | 亚洲精品中文字幕久久久久 | 人妻少妇精品久久 | 亚洲色成人中文字幕网站 | √8天堂资源地址中文在线 | 久久精品成人欧美大片 | 日韩精品乱码av一区二区 | 无码人妻av免费一区二区三区 | 亚洲精品午夜国产va久久成人 | 中文字幕av无码一区二区三区电影 | 内射老妇bbwx0c0ck | 亚洲精品www久久久 | 国产午夜亚洲精品不卡下载 | 麻豆果冻传媒2021精品传媒一区下载 | 波多野结衣 黑人 | 午夜免费福利小电影 | 疯狂三人交性欧美 | yw尤物av无码国产在线观看 | 丰腴饱满的极品熟妇 | 午夜男女很黄的视频 | 日韩精品成人一区二区三区 | 丰满少妇高潮惨叫视频 | 人人妻人人澡人人爽欧美精品 | 久久久av男人的天堂 | 精品人妻人人做人人爽夜夜爽 | 免费播放一区二区三区 | 久热国产vs视频在线观看 | 国产精品久久久 | 久久aⅴ免费观看 | 久久国产精品精品国产色婷婷 | 在线观看免费人成视频 | 国产人妻久久精品二区三区老狼 | 亚洲欧美综合区丁香五月小说 | 丝袜人妻一区二区三区 | √8天堂资源地址中文在线 | 99国产精品白浆在线观看免费 | 日日噜噜噜噜夜夜爽亚洲精品 | 色诱久久久久综合网ywww | a国产一区二区免费入口 | 狂野欧美激情性xxxx | 国产精品爱久久久久久久 | 老子影院午夜精品无码 | 性色欲网站人妻丰满中文久久不卡 | 天天躁日日躁狠狠躁免费麻豆 | 色噜噜亚洲男人的天堂 | 人人爽人人爽人人片av亚洲 | 欧美35页视频在线观看 | 一本久久a久久精品vr综合 | 亚洲乱码国产乱码精品精 | 久久久国产精品无码免费专区 | 国产亚洲日韩欧美另类第八页 | 在线a亚洲视频播放在线观看 | 兔费看少妇性l交大片免费 | 国产午夜无码精品免费看 | 亚洲中文字幕久久无码 | 一本大道伊人av久久综合 | 成人欧美一区二区三区黑人 | 亚洲欧美日韩成人高清在线一区 | 成年美女黄网站色大免费视频 | 鲁鲁鲁爽爽爽在线视频观看 | 丰满人妻一区二区三区免费视频 | 亚洲国产精品无码一区二区三区 | 亚洲成色在线综合网站 | 性色欲网站人妻丰满中文久久不卡 | 少妇被粗大的猛进出69影院 | 日韩成人一区二区三区在线观看 | 人人爽人人澡人人人妻 | 欧美老人巨大xxxx做受 | 国语自产偷拍精品视频偷 | 中文字幕乱码中文乱码51精品 | 牲欲强的熟妇农村老妇女视频 | а√天堂www在线天堂小说 | 九月婷婷人人澡人人添人人爽 | 国产成人精品视频ⅴa片软件竹菊 | 夜夜影院未满十八勿进 | 国产精品美女久久久 | 久久人人爽人人爽人人片ⅴ | 真人与拘做受免费视频一 | 欧美精品一区二区精品久久 | 天天躁夜夜躁狠狠是什么心态 | 永久黄网站色视频免费直播 | 无码国内精品人妻少妇 | 国产精品亚洲五月天高清 | 真人与拘做受免费视频一 | 精品人妻人人做人人爽夜夜爽 | 蜜桃视频韩日免费播放 | 欧美性黑人极品hd | 夜夜影院未满十八勿进 | 亚洲区欧美区综合区自拍区 | 亚洲色无码一区二区三区 | 国产精品igao视频网 | 色诱久久久久综合网ywww | 欧洲熟妇精品视频 | 欧洲精品码一区二区三区免费看 | 欧美成人午夜精品久久久 | 搡女人真爽免费视频大全 | 男女超爽视频免费播放 | 国产小呦泬泬99精品 | 沈阳熟女露脸对白视频 | 亚洲男人av天堂午夜在 | 久久精品国产99精品亚洲 | 99re在线播放 | 日日摸天天摸爽爽狠狠97 | 97夜夜澡人人双人人人喊 | 久久精品人人做人人综合试看 | 久久久久久a亚洲欧洲av冫 | 在线а√天堂中文官网 | 成人女人看片免费视频放人 | 极品嫩模高潮叫床 | 无码成人精品区在线观看 | 中文精品无码中文字幕无码专区 | 无码人妻出轨黑人中文字幕 | 精品无码一区二区三区的天堂 | 少妇人妻av毛片在线看 | 大肉大捧一进一出视频出来呀 | 精品人妻av区 | 成人免费无码大片a毛片 | 精品亚洲成av人在线观看 | 久久伊人色av天堂九九小黄鸭 | 国产午夜福利亚洲第一 | 丰满岳乱妇在线观看中字无码 | 5858s亚洲色大成网站www | 国产成人午夜福利在线播放 | 中文字幕乱码人妻二区三区 | 欧美成人高清在线播放 | 又黄又爽又色的视频 | 国产精品欧美成人 | 无码国产乱人伦偷精品视频 | 久久99精品久久久久久 | 欧美人与善在线com | 欧美 日韩 人妻 高清 中文 | 国产无遮挡吃胸膜奶免费看 | 国产成人综合色在线观看网站 | 国产97人人超碰caoprom | 麻豆果冻传媒2021精品传媒一区下载 | 成人免费视频在线观看 | 人妻无码αv中文字幕久久琪琪布 | 久久人人97超碰a片精品 | 色偷偷人人澡人人爽人人模 | 国产真人无遮挡作爱免费视频 | 亚洲精品中文字幕乱码 | 色狠狠av一区二区三区 | 国产精品成人av在线观看 | 亚洲人成网站免费播放 | 亚洲人交乣女bbw | 国产综合在线观看 | 亚洲国产高清在线观看视频 | 国产精品无码一区二区三区不卡 | 性欧美videos高清精品 | 久久天天躁狠狠躁夜夜免费观看 | 美女极度色诱视频国产 | 中文字幕乱码人妻无码久久 | 综合人妻久久一区二区精品 | 国产办公室秘书无码精品99 | 亚洲国产欧美日韩精品一区二区三区 | av无码久久久久不卡免费网站 | 国产偷自视频区视频 | 国产亚洲精品久久久久久大师 | √天堂资源地址中文在线 | 亚洲第一网站男人都懂 | 国产热a欧美热a在线视频 | 国产精品久久久久9999小说 | 真人与拘做受免费视频 | 国产女主播喷水视频在线观看 | 一本久道久久综合狠狠爱 | 沈阳熟女露脸对白视频 | 国产成人av免费观看 | 77777熟女视频在线观看 а天堂中文在线官网 | 99在线 | 亚洲 | 欧美日韩亚洲国产精品 | 强辱丰满人妻hd中文字幕 | 少妇性荡欲午夜性开放视频剧场 | 欧美刺激性大交 | 荫蒂添的好舒服视频囗交 | 亚洲人交乣女bbw | 性欧美大战久久久久久久 | 亚洲理论电影在线观看 | 99久久无码一区人妻 | 一区二区三区乱码在线 | 欧洲 | 亚拍精品一区二区三区探花 | 无套内谢老熟女 | 久久久久免费精品国产 | 成人精品一区二区三区中文字幕 | 小sao货水好多真紧h无码视频 | 国产莉萝无码av在线播放 | 亚洲欧洲无卡二区视頻 | 久久99精品国产麻豆蜜芽 | 扒开双腿疯狂进出爽爽爽视频 | 中文字幕日产无线码一区 | 国产人妖乱国产精品人妖 | 俺去俺来也www色官网 | 性生交片免费无码看人 | 国产婷婷色一区二区三区在线 | 国产综合久久久久鬼色 | 色婷婷av一区二区三区之红樱桃 | 最新国产麻豆aⅴ精品无码 | 大色综合色综合网站 | 欧美 亚洲 国产 另类 | 精品少妇爆乳无码av无码专区 | 国产精品久久国产三级国 | 九九综合va免费看 | aⅴ亚洲 日韩 色 图网站 播放 | av人摸人人人澡人人超碰下载 | 亚洲天堂2017无码中文 | 熟女少妇在线视频播放 | 国产乱人无码伦av在线a | 扒开双腿疯狂进出爽爽爽视频 | 国产热a欧美热a在线视频 | 久久zyz资源站无码中文动漫 | 国产乱人伦av在线无码 | 国产激情一区二区三区 | 亚洲色大成网站www国产 | 国产亚洲精品久久久闺蜜 | 中文字幕中文有码在线 | 国产精品va在线观看无码 | 日本精品少妇一区二区三区 | 思思久久99热只有频精品66 | 妺妺窝人体色www婷婷 | 一区二区三区乱码在线 | 欧洲 | 漂亮人妻洗澡被公强 日日躁 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲の无码国产の无码步美 | 人人妻人人澡人人爽欧美精品 | 午夜免费福利小电影 | 妺妺窝人体色www婷婷 | 九九热爱视频精品 | 国内综合精品午夜久久资源 | 久久综合给合久久狠狠狠97色 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产精品无码成人午夜电影 | 国产精品手机免费 | 日韩av无码中文无码电影 | 国产卡一卡二卡三 | 波多野结衣av一区二区全免费观看 | 亚洲国产高清在线观看视频 | 精品久久久无码人妻字幂 | 国产黄在线观看免费观看不卡 | 亚洲 激情 小说 另类 欧美 | 中国女人内谢69xxxx | 黄网在线观看免费网站 | 中文久久乱码一区二区 | 亚洲成av人综合在线观看 | 亚洲乱码国产乱码精品精 | 国产精品a成v人在线播放 | 国产亲子乱弄免费视频 | 在线观看国产午夜福利片 | 国产精品人妻一区二区三区四 | 强辱丰满人妻hd中文字幕 | 久久精品人妻少妇一区二区三区 | 亚洲国产高清在线观看视频 | 狠狠色欧美亚洲狠狠色www | 国产午夜无码视频在线观看 | 国产suv精品一区二区五 | 麻豆精品国产精华精华液好用吗 | 少妇人妻偷人精品无码视频 | 婷婷丁香六月激情综合啪 | 宝宝好涨水快流出来免费视频 | 欧美激情一区二区三区成人 | 欧美日韩一区二区免费视频 | 亚洲成熟女人毛毛耸耸多 | 日韩av激情在线观看 | 麻豆精产国品 | 乌克兰少妇性做爰 | 亚洲综合另类小说色区 | 丰满肥臀大屁股熟妇激情视频 | 国产精品爱久久久久久久 | 日日夜夜撸啊撸 | 妺妺窝人体色www婷婷 | 对白脏话肉麻粗话av | 97夜夜澡人人双人人人喊 | 精品国产成人一区二区三区 | 国产真人无遮挡作爱免费视频 | 国产无遮挡又黄又爽又色 | 亚洲一区av无码专区在线观看 | 午夜精品一区二区三区在线观看 | 久久久久免费看成人影片 | 免费无码的av片在线观看 | 午夜无码人妻av大片色欲 | 国产高清不卡无码视频 | 亚洲va中文字幕无码久久不卡 | 99久久婷婷国产综合精品青草免费 | 亚洲精品鲁一鲁一区二区三区 | 又大又紧又粉嫩18p少妇 | 亚洲男女内射在线播放 | 国产9 9在线 | 中文 | 国产精品对白交换视频 | 国产精品内射视频免费 | 国产午夜福利100集发布 | 丰满肥臀大屁股熟妇激情视频 | 在线а√天堂中文官网 | 国产艳妇av在线观看果冻传媒 | 欧美日韩视频无码一区二区三 | 色婷婷香蕉在线一区二区 | 国产亲子乱弄免费视频 | 久久久久久九九精品久 | 四虎国产精品免费久久 | av小次郎收藏 | а√资源新版在线天堂 | 国产色视频一区二区三区 | 国产片av国语在线观看 | 成人免费无码大片a毛片 | 亚洲精品www久久久 | 中文字幕无码热在线视频 | 国产成人无码一二三区视频 | 国产免费无码一区二区视频 | 亚洲精品午夜无码电影网 | 牲欲强的熟妇农村老妇女 | 亚洲成在人网站无码天堂 | 妺妺窝人体色www在线小说 | 高清不卡一区二区三区 | 国产香蕉尹人视频在线 | 午夜福利试看120秒体验区 | 无遮挡啪啪摇乳动态图 | 内射巨臀欧美在线视频 | 亚洲色偷偷男人的天堂 | 国产免费久久精品国产传媒 | 国产亚洲日韩欧美另类第八页 | 麻豆精产国品 | 日本一卡二卡不卡视频查询 | 女高中生第一次破苞av | 少妇人妻偷人精品无码视频 | 国内老熟妇对白xxxxhd | 亚洲春色在线视频 | 精品一区二区不卡无码av | 鲁大师影院在线观看 | 狠狠cao日日穞夜夜穞av | 麻豆国产丝袜白领秘书在线观看 | 亚洲国产精品成人久久蜜臀 | 精品成人av一区二区三区 | 精品亚洲成av人在线观看 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲国产欧美在线成人 | 亚洲精品一区二区三区在线 |