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

笑看風云淡

寵辱不驚,看庭前花開花落;去留無意,望天空云卷云舒
posts - 96, comments - 48, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理

CString類的使用(轉)

Posted on 2009-01-07 13:56 天之驕子 閱讀(11655) 評論(1)  編輯 收藏 引用
先來看看CString的使用:

先定義幾個以后會用到的變量:

CString str1, str2, str3;

概括說明:

    MFC對CString類的封裝可能的確不如std::string完善,但是也的確不錯,功能也足夠強大,使用上還很體貼。其基本特征為:

    CString類沒有基類。

    CString類和LPCTSTR的關系:MSDN上說“CString objects follow "value semantics." Think of a CString object as an actual string, not as a pointer to a string.”也就是說用的時候除了考慮使用它的成員函數的方式,就把它當作普通的c-style字符串來使用就行了。你可以在構造函數中使用LPCTSTR:

        CString str("Hello World!");

可以:

       str1 = str2;                或者   str1 = “Hello”;

也可以:

       str3 = str1 + str2;     或者   str3 = str1 + “Hello”;

當然也可以:

       str1 += str2;              或者       str1 += “Hello”;

實際上下列的這些操作符都可以用在CString對象之間或者CString和LPCTSTR對象之間:

       ==、!=、<、>、<=、>=

自然,將CString對象強制轉換到LPCTSTR類型也就應該在情理之中:

       LPCTSTR string = (LPCTSTR) str1;

    CString支持UNICODE和多字節字符集(MBCS)。因為其本身是基于TCHAR的——當然你不要忘了定義編碼方式,如:#define _UNICODE。

    CString支持引用計數??梢酝ㄟ^其成員函數LockBuffe/和UnLockBuffer來禁用/啟用引用計數。


對于CString類的成員函數的定義、說明、返回值等形式在此并不贅述,如有此疑問請參閱:http://msdn.microsoft.com/library/en-us/vcmfc98/html/_mfc_cstring_class_members.asp中的相關鏈接。

常用函數和范例:

改變大小寫:

CString::MakeUpper和CString::MakeLower兩個成員函數(不帶參數)能使整個字符串變成大/小寫字母。

例:       str1 = “hello”;

               str1.MakeUpper();

               afxDump << str1;         // 輸出結果是”HELLO”;

反轉:void CString::MakeReverse();

從.rc文件讀入字符串:

CString::LoadString函數把傳入的字符串資源ID對應的字符串讀入到CString對象中。如果成功就返回非零值。

       BOOL bResult = LoadString(IDS_FILENOTFOUND);

子串操作

→去掉字符串左邊空格:str1.TrimLeft();

→去掉字符串右邊空格:str1.TrimRight();

→獲得指定位置字符:char a = str1.GetAt(3); 相應的有CString::SetAt函數,修改指定位置字符。

→刪除字符串中所有指定字符:

       str1 = “Hello test”;

       str1.Remove(‘t’);

       afxDump << str1;         //輸出”Hello es”;

→刪除指定位置指定長度的子串:

       str1 = “Hello test”;

       str1.Delete(3, 2);           //第一個參數為index(從零開始)

                                          //第二個參數是長度

       afxDump << str1;         //輸出”Hel test”;

→清空對象的內容:

void CString::Empty();

→查找子串:

※CString::Find函數有四個重載,可以進行字符和字串的搜索,同時還可以指定搜索的起始位置,并返回第一次查找到的index。

int Find( TCHAR ch ) const;

int Find( LPCTSTR lpszSub ) const;

int Find( TCHAR ch, int nStart ) const;

int Find( LPCTSTR pstr, int nStart ) const;

※CString::ReverseFind是返回字符串中最后一個匹配字符的index,與Find函數查找方向正好相反,可惜只有一種重載:

int ReverseFind( TCHAR ch ) const;

※CString::FindOneof查找的是第一個與指定字符串中任意一個匹配字符的index。(好像有點繞口,看看例子就明白了)

       str1 = “Hello test”;

       int j = str1.Find(“el”);

       afxDump << “j=” << j << “\n”;

       int k = str1.Find(‘e’, 3);

       afxDump << “k=” << k << “\n”;

       int l = str1.ReverseFind(‘t’);

       afxDump << “l=” << l << “\n”;

       int m = str1.ReverseFind(‘t’);

       afxDump << “m=” << m << “\n”;

       int n = str1. FindOneOf(“stuv”);

       afxDump << “n=” << n << “\n”;

輸出結果:

       j=1

       k=7

       l=9

       m=9

       n=6

→字串截斷:CString::Left、CString::Right函數都只帶一個參數,并且都返回一個CString對象,作用是截取左/右邊指定長度的子串。CString::Mid函數第一個參數指定位置,第二個參數指定長度。這幾個都是常用的函數,就不寫例子了

→獲得Buffer

經常有人問到CString對象和char *的轉換問題,除了前面說到的強制轉化,就是用這個了

LPTSTR GetBufferSetLength( int nNewLength );使用返回的指針可以直接修改CString對象的內容,不過有兩點要注意,一是如果指定長度比原CString長度短(截斷)請記得在后面補’\0’,二是在調用CString對象的任何其它成員函數前請一定記得ReleaseBuffer,也許不用似乎并沒有出錯,但是說不定就是大隱患的根源。

→CString::SpanExcluding函數

以前回答過一個把CString對象分離子串的問題,現在想想,如果當時用這個函數的話,將使多么的方便。函數原型:

CString SpanExcluding( LPCTSTR lpszCharSet ) const;

它查找CString對象中與lpszCharSet串中任意匹配的第一個字符,并返回一個CString對象,該對象的內容是原來對象從起始位置到查找到字符的前一個字符的部分。這在分離用分割符(逗號空格之類)隔開的子串的時候將十分方便:

       str1 = “Hello test”;

       str2 = str1.SpanExcluding(“ ,”);

       afxDump << str2;         //輸出”Hello”

同時,還有一個很方便的函數:CString::SpanIncluding,函數原型:

CString SpanIncluding( LPCTSTR lpszCharSet ) const;

它返回對象中前若干個字符,這些字符都必須在lpszCharSet之中:

       str1 = “Hello test”;

       str2 = str1.SpanIncluding(“ABCDEFGHIJK”);

       afxDump << str2;         //輸出”H”

→插入子串:用CString::Insert可以插入字符或者字串到指定位置

       str1 = “Hello test”;

       str1.Insert(2,“ABCD”);

       afxDump << str1;         //輸出”HeABCDllo test”

→替換:CString::Replace的作用是將原來對象中的所有匹配相替換指定字符/子串。有兩個重載原型:

int Replace( TCHAR chOld, TCHAR chNew );

int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );

☆CString對象的屬性操作:這些都很常用了,簡要說明之

int GetLength( ) const;          //獲得buffer的長度

BOOL IsEmpty( ) const;              //判斷CString對象內容是否為空

int Compare( LPCTSTR lpsz ) const;   //與lpsz按ASCII碼比較

int CompareNoCase( LPCTSTR lpsz ) const;             //與lpsz按ASCII碼比較,忽略大小寫

CString::Format             /*用來格式化對象。切記不要把對象本身放到Format函數的參數中去了*/

原理:

Cstring就是對一個用來存放字符串的緩沖區和對施加于這個字符串的操作封裝。也就是說,Cstring里需要有一個用來存放字符串的緩沖區,并且有一個指針指向該緩沖區,該指針就是LPTSTR m_pchData。但是有些字符串操作會增建或減少字符串的長度,因此為了減少頻繁的申請內存或者釋放內存,Cstring會先申請一個大的內存塊用來存放字符串。這樣,以后當字符串長度增長時,如果增加的總長度不超過預先申請的內存塊的長度,就不用再申請內存。當增加后的字符串長度超過預先申請的內存時,Cstring先釋放原先的內存,然后再重新申請一個更大的內存塊。同樣的,當字符串長度減少時,也不釋放多出來的內存空間。而是等到積累到一定程度時,才一次性將多余的內存釋放。

還有,當使用一個Cstring對象a來初始化另一個Cstring對象b時,為了節省空間,新對象b并不分配空間,它所要做的只是將自己的指針指向對象a的那塊內存空間,只有當需要修改對象a或者b中的字符串時,才會為新對象b申請內存空間,這叫做寫入復制技術(CopyBeforeWrite)。

這樣,僅僅通過一個指針就不能完整的描述這塊內存的具體情況,需要更多的信息來描述。

首先,需要有一個變量來描述當前內存塊的總的大小。

其次,需要一個變量來描述當前內存塊已經使用的情況。也就是當前字符串的長度

另外,還需要一個變量來描述該內存塊被其他Cstring引用的情況。有一個對象引用該內存塊,就將該數值加一。

Cstring中專門定義了一個結構體來描述這些信息:

struct CStringData

{

   long nRefs;             // reference count

   int nDataLength;        // length of data (including terminator)

   int nAllocLength;       // length of allocation

   // TCHAR data[nAllocLength]

   TCHAR* data()           // TCHAR* to managed data

               { return (TCHAR*)(this+1); }

};

實際使用時,該結構體的所占用的內存塊大小是不固定的,在Cstring內部的內存塊頭部,放置的是該結構體。從該內存塊頭部開始的sizeof(CstringData)個BYTE后才是真正的用于存放字符串的內存空間。這種結構的數據結構的申請方法是這樣實現的:

pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];

pData->nAllocLength = nLen;

其中nLen是用于說明需要一次性申請的內存空間的大小的。

從代碼中可以很容易的看出,如果想申請一個256個TCHAR的內存塊用于存放字符串,實際申請的大小是:

sizeof(CstringData)個BYTE + (nLen+1)個TCHAR

其中前面sizeof(CstringData)個BYTE是用來存放CstringData信息的。后面的nLen+1個TCHAR才是真正用來存放字符串的,多出來的一個用來存放’\0’。

Cstring中所有的operations的都是針對這個緩沖區的。比如LPTSTR CString::GetBuffer(int nMinBufLength),它的實現方法是:

首先通過Cstring::GetData()取得CstringData對象的指針。該指針是通過存放字符串的指針m_pchData先后偏移sizeof(CstringData),從而得到了CstringData的地址。

然后根據參數nMinBufLength給定的值重新實例化一個CstringData對象,使得新的對象里的字符串緩沖長度能夠滿足nMinBufLength。

然后在重新設置一下新的CstringData中的一些描述值。

最后將新CstringData對象里的字符串緩沖直接返回給調用者。

這些過程用C++代碼描述就是:

   if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)

   {

               // we have to grow the buffer

               CStringData* pOldData = GetData();

               int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it

               if (nMinBufLength < nOldLen)

                           nMinBufLength = nOldLen;

               AllocBuffer(nMinBufLength);

               memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));

               GetData()->nDataLength = nOldLen;

               CString::Release(pOldData);

   }

   ASSERT(GetData()->nRefs <= 1);

   // return a pointer to the character storage for this string

   ASSERT(m_pchData != NULL);

   return m_pchData;

很多時候,我們經常的對大批量的字符串進行互相拷貝修改等,Cstring 使用了CopyBeforeWrite技術。使用這種方法,當利用一個Cstring對象a實例化另一個對象b的時候,其實兩個對象的數值是完全相同的,但是如果簡單的給兩個對象都申請內存的話,對于只有幾個、幾十個字節的字符串還沒有什么,如果是一個幾K甚至幾M的數據量來說,是一個很大的浪費。

因此Cstring 在這個時候只是簡單的將新對象b的字符串地址m_pchData直接指向另一個對象a的字符串地址m_pchData。所做的額外工作是將對象a的內存應用CstringData:: nRefs加一。

CString::CString(const CString& stringSrc)

{

               m_pchData = stringSrc.m_pchData;

               InterlockedIncrement(&GetData()->nRefs);

}


這樣當修改對象a或對象b的字符串內容時,首先檢查CstringData:: nRefs的值,如果大于一(等于一,說明只有自己一個應用該內存空間),說明該對象引用了別的對象內存或者自己的內存被別人應用,該對象首先將該應用值減一,然后將該內存交給其他的對象管理,自己重新申請一塊內存,并將原來內存的內容拷貝過來。

其實現的簡單代碼是:

void CString::CopyBeforeWrite()

{

   if (GetData()->nRefs > 1)

   {

               CStringData* pData = GetData();

               Release();

               AllocBuffer(pData->nDataLength);

memcpy(m_pchData, pData->data(),

            (pData- >nDataLength+1)*sizeof(TCHAR));

   }

}

其中Release 就是用來判斷該內存的被引用情況的。

void CString::Release()

{

   if (GetData() != _afxDataNil)

   {

               if (InterlockedDecrement(&GetData()->nRefs) <= 0)

                           FreeData(GetData());

   }

}

當多個對象共享同一塊內存時,這塊內存就屬于多個對象,而不在屬于原來的申請這塊內存的那個對象了。但是,每個對象在其生命結束時,都首先將這塊內存的引用減一,然后再判斷這個引用值,如果小于等于零時,就將其釋放,否則,將之交給另外的正在引用這塊內存的對象控制。

Cstring使用這種數據結構,對于大數據量的字符串操作,可以節省很多頻繁申請釋放內存的時間,有助于提升系統性能。

通過上面的分析,我們已經對Cstring的內部機制已經有了一個大致的了解了??偟恼f來MFC中的Cstring是比較成功的。但是,由于數據結構比較復雜(使用CstringData),所以在使用的時候就出現了很多的問題,最典型的一個就是用來描述內存塊屬性的屬性值和實際的值不一致。出現這個問題的原因就是Cstring為了方便某些應用,提供了一些operations,這些operation可以直接返回內存塊中的字符串的地址值,用戶可以通過對這個地址值指向的地址進行修改,但是,修改后又沒有調用相應的operations1使CstringData中的值來保持一致。比如,用戶可以首先通過operations得到字符串地址,然后將一些新的字符增加到這個字符串中,使得字符串的長度增加,但是,由于是直接通過指針修改的,所以描述該字符串長度的CstringData中的nDataLength卻還是原來的長度,因此當通過GetLength獲取字符串長度時,返回的必然是不正確的。

存在這些問題的operations下面一一介紹。

1.      GetBuffer

很多錯誤用法中最典型的一個就是Cstring:: GetBuffer ()了.查了MSDN,里面對這個operation的描述是:

Returns a pointer to the internal character buffer for the CString object. The returned LPTSTR is not const and thus allows direct modification of CString contents。

這段很清楚的說明,對于這個operation返回的字符串指針,我們可以直接修改其中的值:

   CString str1("This is the string 1");――――――――――――――――1

   int nOldLen = str1.GetLength();―――――――――――――――――2

   char* pstr1 = str1.GetBuffer( nOldLen );――――――――――――――3

   strcpy( pstr1, "modified" );――――――――――――――――――――4

   int nNewLen = str1.GetLength();―――――――――――――――――5

通過設置斷點,我們來運行并跟蹤這段代碼可以看出,當運行到三處時,str1的值是”This is the string 1”,并且nOldLen的值是20。當運行到5處時,發現,str1的值變成了”modified”。也就是說,對GetBuffer返回的字符串指針,我們將它做為參數傳遞給strcpy,試圖來修改這個字符串指針指向的地址,結果是修改成功,并且Cstring對象str1的值也響應的變成了” modified”。但是,我們接著再調用str1.GetLength()時卻意外的發現其返回值仍然是20,但是實際上此時str1中的字符串已經變成了” modified”,也就是說這個時候返回的值應該是字符串” modified”的長度8!而不是20?,F在Cstring工作已經不正常了!這是怎么回事?

很顯然,str1工作不正常是在對通過GetBuffer返回的指針進行一個字符串拷貝之后的。

再看MSDN上的關于這個operation的說明,可以看到里面有這么一段話:

If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CString member s.

原來在對GetBuffer返回的指針使用之后需要調用ReleaseBuffer,這樣才能使用其他Cstring的operations。上面的代碼中,我們在4-5處增建一行代碼:str2.ReleaseBuffer(),然后再觀察nNewLen,發現這個時候已經是我們想要的值8了。

從Cstring的機理上也可以看出:GetBuffer返回的是CstringData對象里的字符串緩沖的首地址。根據這個地址,我們對這個地址里的值進行的修改,改變的只是CstringData里的字符串緩沖中的值, CstringData中的其他用來描述字符串緩沖的屬性的值已經不是正確的了。比如此時CstringData:: nDataLength很顯然還是原來的值20,但是現在實際上字符串的長度已經是8了。也就是說我們還需要對CstringData中的其他值進行修改。這也就是需要調用ReleaseBuffer()的原因了。

正如我們所預料的,ReleaseBuffer源代碼中顯示的正是我們所猜想的:

   CopyBeforeWrite(); // just in case GetBuffer was not called

   if (nNewLength == -1)

               nNewLength = lstrlen(m_pchData); // zero terminated

   ASSERT(nNewLength <= GetData()->nAllocLength);

   GetData()->nDataLength = nNewLength;

   m_pchData[nNewLength] = ''''\0'''';

其中CopyBeforeWrite是實現寫拷貝技術的,這里不管它。

下面的代碼就是重新設置CstringData對象中描述字符串長度的那個屬性值的。首先取得當前字符串的長度,然后通過GetData()取得CstringData的對象指針,并修改里面的nDataLength成員值。

但是,現在的問題是,我們雖然知道了錯誤的原因,知道了當修改了GetBuffer返回的指針所指向的值之后需要調用ReleaseBuffer才能使用Cstring的其他operations時,我們就能避免不在犯這個錯誤了。答案是否定的。這就像雖然每一個懂一點編程知識的人都知道通過new申請的內存在使用完以后需要通過delete來釋放一樣,道理雖然很簡單,但是,最后實際的結果還是有由于忘記調用delete而出現了內存泄漏。
實際工作中,常常是對GetBuffer返回的值進行了修改,但是最后卻忘記調用ReleaseBuffer來釋放。而且,由于這個錯誤不象new和delete人人都知道的并重視的,因此也沒有一個檢查機制來專門檢查,所以最終程序中由于忘記調用ReleaseBuffer而引起的錯誤被帶到了發行版本中。

要避免這個錯誤,方法很多。但是最簡單也是最有效的就是避免這種用法。很多時候,我們并不需要這種用法,我們完全可以通過其他的安全方法來實現。

比如上面的代碼,我們完全可以這樣寫:

   CString str1("This is the string 1");

   int nOldLen = str1.GetLength();

   str1 = "modified";

   int nNewLen = str1.GetLength();

但是有時候確實需要,比如:

我們需要將一個Cstring對象中的字符串進行一些轉換,這個轉換是通過調用一個dll里的函數Translate來完成的,但是要命的是,不知道什么原因,這個函數的參數使用的是char*型的:

DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );

這個時候我們可能就需要這個方法了:

Cstring strDest;

Int nDestLen = 100;

DWORD dwRet = Translate( _strSrc.GetBuffer( _strSrc.GetLength() ),

strDest.GetBuffer(nDestLen),

_strSrc.GetLength(), nDestlen );

_strSrc.ReleaseBuffer();

strDest.ReleaseBuffer();

if ( SUCCESSCALL(dwRet) )

{

}

if ( FAILEDCALL(dwRet) )

{

}

的確,這種情況是存在的,但是,我還是建議盡量避免這種用法,如果確實需要使用,請不要使用一個專門的指針來保存GetBuffer返回的值,因為這樣常常會讓我們忘記調用ReleaseBuffer。就像上面的代碼,我們可以在調用GetBuffer之后馬上就調用ReleaseBuffer來調整Cstring對象。

2.      LPCTSTR

關于LPCTSTR的錯誤常常發生在初學者身上。

例如在調用函數

DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );

時,初學者常常使用的方法就是:

int nLen = _strSrc.GetLength();

DWORD dwRet = Translate( (char*)(LPCTSTR)_strSrc),

(char*)(LPCTSTR)_strSrc),

nLen,

nLen);

if ( SUCCESSCALL(dwRet) )

{

}

if ( FAILEDCALL(dwRet) )

{

}

他原本的初衷是將轉換后的字符串仍然放在_strSrc中,但是,當調用完Translate以后之后再使用_strSrc時,卻發現_strSrc已經工作不正常了。檢查代碼卻又找不到問題到底出在哪里。

其實這個問題和第一個問題是一樣的。Cstring類已經將LPCTST重載了。在Cstring中LPCTST實際上已經是一個operation了。對LPCTST的調用實際上和GetBuffer是類似的,直接返回CstringData對象中的字符串緩沖的首地址。

其C++代碼實現是:

_AFX_INLINE CString::operator LPCTSTR() const

   { return m_pchData; }

因此在使用完以后同樣需要調用ReleaseBuffer()。

但是,這個誰又能看出來呢?

其實這個問題的本質原因出在類型轉換上。LPCTSTR返回的是一個const char*類型,因此使用這個指針來調用Translate編譯是不能通過的。對于一個初學者,或者一個有很長編程經驗的人都會再通過強行類型轉換將const char*轉換為char*。最終造成了Cstring工作不正常,并且這樣也很容易造成緩沖溢出。

通過上面對于Cstring機制和一些容易出現的使用錯誤的描述,可以使我們更好的使用Cstring

Feedback

# re: CString類的使用(轉)  回復  更多評論   

2009-08-14 20:31 by ss
總結的不錯

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区欧美亚洲| 欧美另类videos死尸| 欧美大片在线看| 欧美成在线视频| 亚洲国产精品一区在线观看不卡 | 久久国产欧美日韩精品| 欧美综合国产精品久久丁香| 久久九九久精品国产免费直播 | 91久久黄色| 一区二区高清| 香蕉av777xxx色综合一区| 久久精品国产69国产精品亚洲| 久久久美女艺术照精彩视频福利播放 | 亚洲欧美国产精品桃花| 久久精品国产69国产精品亚洲| 欧美在线视频观看| 亚洲欧美一区二区三区在线| 一区二区三区www| 欧美一区二区三区四区在线观看地址| 欧美一区二区三区播放老司机| 欧美1区3d| 国产一区二区视频在线观看| 亚洲六月丁香色婷婷综合久久| 午夜免费在线观看精品视频| 亚洲电影网站| 欧美伊人久久久久久久久影院| 欧美成人精品一区二区| 国产日韩欧美在线| 99re8这里有精品热视频免费| 久久se精品一区精品二区| 亚洲国产一区二区三区青草影视 | 国产精品久久久久久久久免费桃花 | 另类国产ts人妖高潮视频| 日韩一级黄色av| 另类春色校园亚洲| 国产亚洲欧美日韩日本| 亚洲一区国产精品| 最新热久久免费视频| 久久免费视频一区| 国产无一区二区| 香蕉久久a毛片| 中文久久乱码一区二区| 欧美日韩国产麻豆| 亚洲久久在线| 免费在线观看一区二区| 欧美一区二区三区在线视频 | 国产美女精品免费电影| 亚洲欧美三级在线| 中文一区二区| 欧美天堂亚洲电影院在线观看| 亚洲久久视频| 亚洲黄色免费网站| 免费欧美在线视频| 亚洲国产精品va在线看黑人 | 国内精品美女在线观看| 香蕉久久国产| 午夜精品久久久久久久白皮肤| 国产精品v亚洲精品v日韩精品 | 国产精品免费视频观看| 在线一区二区三区做爰视频网站| 最新国产乱人伦偷精品免费网站| 国内精品久久久久影院色| 欧美一区二区三区视频免费| 在线亚洲观看| 国产精品卡一卡二| 久久成人精品一区二区三区| 亚洲永久在线| 国语精品中文字幕| 麻豆久久久9性大片| 久久久久久夜精品精品免费| 亚洲高清不卡av| 亚洲日韩成人| 欧美午夜视频在线观看| 欧美一区二区三区免费大片| 午夜精品国产更新| 国内精品免费午夜毛片| 亚洲成色999久久网站| 欧美高清hd18日本| 亚洲综合清纯丝袜自拍| 羞羞答答国产精品www一本| 精品二区视频| 亚洲欧洲一级| 国产精品久久久久久久久久三级 | 好吊视频一区二区三区四区| 免费毛片一区二区三区久久久| 免费人成网站在线观看欧美高清| 一区二区三区成人| 欧美一区二区三区男人的天堂| 亚洲国产精品一区二区第四页av| 亚洲国产欧美日韩| 国产精品婷婷| 欧美激情在线免费观看| 国产精品久久久久永久免费观看 | 欧美一级视频| 日韩视频国产视频| 午夜宅男欧美| 一区二区欧美视频| 久久丁香综合五月国产三级网站| 亚洲精品小视频在线观看| 亚洲一区在线直播| 亚洲黄一区二区| 羞羞视频在线观看欧美| 日韩一级精品| 久久综合五月天婷婷伊人| 亚洲综合视频网| 欧美国产日韩亚洲一区| 久久久久国产一区二区三区四区| 欧美男人的天堂| 欧美岛国激情| 一区二区三区无毛| 欧美一区二视频| 亚洲欧美在线一区二区| 欧美精品激情在线观看| 快播亚洲色图| 国产亚洲一级高清| 一区二区成人精品| 亚洲一区欧美二区| 亚洲国产精品尤物yw在线观看| 亚洲深夜福利| 亚洲视频中文| 欧美人与性动交a欧美精品| 嫩草影视亚洲| 亚洲丶国产丶欧美一区二区三区 | 亚洲激情不卡| 亚洲激情网站| 蜜臀va亚洲va欧美va天堂| 久久中文字幕导航| 国色天香一区二区| 小黄鸭精品密入口导航| 欧美一级二级三级蜜桃| 国产精品推荐精品| 亚洲一区图片| 欧美在线视频观看| 国产亚洲精品v| 欧美怡红院视频| 久久精品免视看| 激情成人中文字幕| 久久一区视频| 亚洲电影免费| 一区二区三区欧美成人| 欧美亚一区二区| 亚洲综合第一页| 久久久视频精品| 亚洲大胆人体视频| 欧美电影免费观看高清| 亚洲精品极品| 性久久久久久久| 黄色精品网站| 欧美二区在线| 一区二区三欧美| 久久精品日产第一区二区| 狠狠综合久久av一区二区老牛| 久久嫩草精品久久久久| 亚洲国内在线| 性一交一乱一区二区洋洋av| 国产日韩精品在线| 开心色5月久久精品| 亚洲精品乱码视频| 羞羞色国产精品| 在线日本成人| 欧美手机在线| 久久久久久久一区二区三区| 亚洲国产精品一区制服丝袜| 亚洲影院免费| 尤物yw午夜国产精品视频明星 | 欧美人与性动交a欧美精品| 亚洲网站在线观看| 久久综合福利| 亚洲视频精选| 又紧又大又爽精品一区二区| 欧美大片专区| 欧美在线1区| 日韩视频在线一区二区| 久久久一区二区三区| 一区二区三区四区五区精品| 国产欧美日韩综合一区在线观看| 久久伊人精品天天| 亚洲一区激情| 亚洲日本成人| 鲁鲁狠狠狠7777一区二区| 亚洲素人在线| 日韩天堂av| 亚洲第一色在线| 国产精品私拍pans大尺度在线| 在线视频精品一区| 亚洲激情专区| 国产手机视频一区二区| 欧美片第一页| 老司机凹凸av亚洲导航| 午夜精品成人在线视频| 亚洲免费观看高清完整版在线观看熊 | 欧美激情一区二区三区蜜桃视频 | 一本大道久久a久久精二百| 男人的天堂亚洲在线| 久久av一区二区| 亚洲欧美影音先锋| 99视频日韩| 亚洲国产成人精品视频| 国精品一区二区| 国产精品日韩一区二区|