日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

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

编程问答

CString Management (关于CString的所有操作)

發布時間:2025/3/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CString Management (关于CString的所有操作) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

CString Management (關于CString的所有操作)
作者:FreeEIM
CStrings are a useful data type. They greatly simplify a lot of operations in MFC, making it much more convenient to do string manipulation. However, there are some special techniques to using CStrings, particularly hard for people coming from a pure-C background to learn. This essay discusses some of these techniques.

Much of what you need to do is pretty straightforward. This is not a complete tutorial on CStrings, but captures the most common basic questions.

CString concatenation
Formatting (including integer-to-CString)
Converting CStrings to integers?
Converting between char * to a CString?
???? char * to CString
???? CString to char * I: Casting to LPCTSTR
???? CString to char * II: Using GetBuffer
???? CString to char * III: Interfacing to a control
CString to BSTR??
BSTR to CString? (30-Jan-01)
VARIANT to CString?? (24-Feb-01)
Loading STRINGTABLE resources? (22-Feb-01)
CStrings and temporary objects??
CString efficiency
String ConcatenationOne of the very convenient features of CString is the ability to concatenate two strings. For example if we have

CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;
is a lot nicer than having to do something like:

char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);
Formatting (including integer-to-CString)
Rather than using sprintf or wsprintf, you can do formatting for a CString by using the Format method:

CString s;
s.Format(_T("The total is %d"), total);
The advantage here is that you don't have to worry about whether or not the buffer is large enough to hold the formatted data; this is handled for you by the formatting routines.

Use of formatting is the most common way of converting from non-string data types to a CString, for example, converting an integer to a CString:

CString s;
s.Format(_T("%d"), total);
I always use the _T( ) macro because I design my programs to be at least Unicode-aware, but that's a topic for some other essay. The purpose of _T( ) is to compile a string for an 8-bit-character application as:

#define _T(x) x // non-Unicode version
whereas for a Unicode application it is defined as

#define _T(x) L##x // Unicode version
so in Unicode the effect is as if I had written

s.Format(L"%d", total);
If you ever think you might ever possibly use Unicode, start coding in a Unicode-aware fashion. For example, never, ever use sizeof( ) to get the size of a character buffer, because it will be off by a factor of 2 in a Unicode application. We cover Unicode in some detail in Win32 Programming. When I need a size, I have a macro called DIM, which is defined in a file dim.h that I include everywhere:

#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
This is not only useful for dealing with Unicode buffers whose size is fixed at compile time, but any compile-time defined table.

class Whatever { ... };
Whatever data[] = {
?? { ... },
??? ...
?? { ... },
};

for(int i = 0; i < DIM(data); i++) // scan the table looking for a match
Beware of those API calls that want genuine byte counts; using a character count will not work.

TCHAR data[20];
lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
This is because lstrcpyn wants a character count, but WriteFile wants a byte count. Also note that this always writes out the entire contents of data. If you only want to write out the actual length of the data, you would think you might do

WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
but that will not work in a Unicode application. Instead, you must do

WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
because WriteFile wants a byte count. (For those of you who might be tempted to say "but that means I'll always be multiplying by 1 for ordinary applications, and that is inefficient", you need to understand what compilers actually do. No real C or C++ compiler would actually compile a multiply instruction inline; the multiply-by-one is simply discarded by the compiler as being a silly thing to do. And if you think when you use Unicode that you'll have to pay the cost of multiplying by 2, remember that this is just a bit-shift left by 1 bit, which the compiler is also happy to do instead of the multiplication).

Using _T does not create a Unicode application. It creates a Unicode-aware application. When you compile in the default 8-bit mode, you get a "normal" 8-bit program; when you compile in Unicode mode, you get a Unicode (16-bit-character) application. Note that a CString in a Unicode application is a string that holds 16-bit characters.

Converting a CString to an integerThe simplest way to convert a CString to an integer value is to use one of the standard string-to-integer conversion routines.

While generally you will suspect that _atoi is a good choice, it is rarely the right choice. If you play to be Unicode-ready, you should call the function _ttoi, which compiles into _atoi in ANSI code and _wtoi in Unicode code. You can also consider using _tcstoul (for unsigned conversion to any radix, such as 2, 8, 10 or 16) or _tcstol (for signed conversion to any radix). For example, here are some examples:

CString hex = _T("FAB");
CString decimal = _T("4011");
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
Converting between char * and CStringThis is the most common set of questions beginners have on the CString data type. Due largely to serious C++ magic, you can largely ignore many of the problems. Things just "work right". The problems come about when you don't understand the basic mechanisms and then don't understand why something that seems obvious doesn't work.

For example, having noticed the above example you might wonder why you can't write

CString graycat = "Gray" + "Cat";
or

CString graycat("Gray" + "Cat");
In fact the compiler will complain bitterly about these attempts. Why? Because the + operator is defined as an overloaded operator on various combinations of the CString and LPCTSTR data types, but not between two LPCTSTR data types, which are underlying data types. You can't overload C++ operators on base types like int and char, or char *. What will work is

CString graycat = CString("Gray") + CString("Cat");
or even

CString graycat = CString("Gray") + "Cat";
If you study these, you will see that the + always applies to at least one CString and one LPCSTR.

char * to CStringSo you have a char *, or a string. How do you create a CString. Here are some examples:

char * p = "This is a test"
or, in Unicode-aware applications

TCHAR * p = _T("This is a test")
or

LPTSTR p = _T("This is a test");
you can write any of the following:

CString s = "This is a test";???? // 8-bit only
CString s = _T("This is a test"); // Unicode-aware
CString s("This is a test");????? // 8-bit only
CSTring s(_T("This is a test");?? // Unicode-aware
CString s = p;
CString s(p);
Any of these readily convert the constant string or the pointer to a CString value. Note that the characters assigned are always copied into the CString so that you can do something like

TCHAR * p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;
and be sure that the resulting string is "GrayCat".

There are several other methods for CString constructors, but we will not consider most of these here; you can read about them on your own.

CString to char * I: Casting to LPCTSTRThis is a slightly harder transition to find out about, and there is lots of confusion about the "right" way to do it. There are quite a few right ways, and probably an equal number of wrong ways.

The first thing you have to understand about a CString is that it is a special C++ object which contains three values: a pointer to a buffer, a count of the valid characters in the buffer, and a buffer length. The count of the number of characters can be any size from 0 up to the maximum length of the buffer minus one (for the NUL byte). The character count and buffer length are cleverly hidden.

Unless you do some special things, you know nothing about the size of the buffer that is associated with the CString. Therefore, if you can get the address of the buffer, you cannot change its contents. You cannot shorten the contents, and you absolutely must not lengthen the contents. This leads to some at-first-glance odd workarounds.

The operator LPCTSTR (or more specifically, the operator const TCHAR *), is overloaded for CString. The definition of the operator is to return the address of the buffer. Thus, if you need a string pointer to the CString you can do something like

CString s("GrayCat");
LPCTSTR p =? s;
and it works correctly. This is because of the rules about how casting is done in C; when a cast is required, C++ rules allow the cast to be selected. For example, you could define (float) as a cast on a complex number (a pair of floats) and define it to return only the first float (called the "real part") of the complex number so you could say

Complex c(1.2f, 4.8f);
float realpart = c;
and expect to see, if the (float) operator is defined properly, that the value of realpart is now 1.2.

This works for you in all kinds of places. For example, any function that takes an LPCTSTR parameter will force this coercion, so that you can have a function (perhaps in a DLL you bought):

BOOL DoSomethingCool(LPCTSTR s);
and call it as follows

CString file("c://myfiles//coolstuff")
BOOL result = DoSomethingCool(file);
This works correctly because the DoSomethingCool function has specified that it wants an LPCTSTR and therefore the LPCTSTR operator is applied to the argument, which in MFC means that the address of the string is returned.

But what if you want to format it?

CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s", graycat);
Note that because the value appears in the variable-argument list (the list designated by "..." in the specification of the function) that there is no implicit coercion operator. What are you going to get?

Well, surprise, you actually get the string

"Mew! I love GrayCat"
because the MFC implementers carefully designed the CString data type so that an expression of type CString evaluates to the pointer to the string, so in the absence of any casting, such as in a Format or sprintf, you will still get the correct behavior. The additional data that describes a CString actually lives in the addresses below the nominal CString address.

What you can't do is modify the string. For example, you might try to do something like replace the "." by a "," (don't do it this way, you should use the National Language Support features for decimal conversions if you care about internationalization, but this makes a simple example):

CString v("1.00");? // currency amount, 2 decimal places
LPCTSTR p = v;
p[lstrlen(p) - 3] = ',';
If you try to do this, the compiler will complain that you are assigning to a constant string. This is the correct message. It would also complain if you tried

strcat(p, "each");
because strcat wants an LPTSTR as its first argument and you gave it an LPCTSTR.

Don't try to defeat these error messages. You will get yourself into trouble!

The reason is that the buffer has a count, which is inaccessible to you (it's in that hidden area that sits below the CString address), and if you change the string, you won't see the change reflected in the character count for the buffer. Furthermore, if the string happens to be just about as long as the buffer physical limit (more on this later), an attempt to extend the string will overwrite whatever is beyond the buffer, which is memory you have no right to write (right?) and you'll damage memory you don't own. Sure recipe for a dead application.

CString to char * II: Using GetBufferA special method is available for a CString if you need to modify it. This is the operation GetBuffer. What this does is return to you a pointer to the buffer which is considered writeable. If you are only going to change characters or shorten the string, you are now free to do so:

CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p, '.'); // OK, should have used s.Find...
if(p != NULL)
??? *p = _T('/0');
s.ReleaseBuffer();
This is the first and simplest use of GetBuffer. You don't supply an argument, so the default of 0 is used, which means "give me a pointer to the string; I promise to not extend the string". When you call ReleaseBuffer, the actual length of the string is recomputed and stored in the CString. Within the scope of a GetBuffer/ReleaseBuffer sequene, and I emphasize this: You Must Not, Ever, Use Any Method Of CString on the CString whose buffer you have! The reason for this is that the integrity of the CString object is not guaranteed until the ReleaseBuffer is called. Study the code below:

CString s(...);
LPTSTR p = s.GetBuffer();
//... lots of things happen via the pointer p
int n = s.GetLength(); // BAD!!!!! PROBABLY WILL GIVE WRONG ANSWER!!!
s.TrimRight();???????? // BAD!!!!! NO GUARANTEE IT WILL WORK!!!!
s.ReleaseBuffer();???? // Things are now OK
int m = s.GetLength(); // This is guaranteed to be correct
s.TrimRight();???????? // Will work correctly
Suppose you want to actually extend the string. In this case you must know how large the string will get. This is just like declaring

char buffer[1024];
knowing that 1024 is more than enough space for anything you are going to do. The equivalent in the CString world is

LPTSTR p = s.GetBuffer(1024);
This call gives you not only a pointer to the buffer, but guarantees that the buffer will be (at least) 1024 bytes in length.

Also, note that if you have a pointer to a const string, the string value itself is stored in read-only memory; an attempt to store into it, even if you've done GetBuffer, you have a pointer to read-only memory, so an attempt to store into the string will fail with an access error. I haven't verified this for CString, but I've seen ordinary C programmers make this error frequently.

A common "bad idiom" left over from C programmers is to allocate a buffer of fixed size, do a sprintf into it, and assign it to a CString:

char buffer[256];
sprintf(buffer, "%......", args, ...); // ... means "lots of stuff here"
CString s = buffer;
while the better form is to do

CString s;
s.Format(_T("%....", args, ...);
Note that this always works; if your string happens to end up longer than 256 bytes you don't clobber the stack!

Another common error is to be clever and realize that a fixed size won't work, so the programmer allocates bytes dynamically. This is even sillier:

int len = lstrlen(parm1) + 13 + lstrlen(parm2) + 10 + 100;
char * buffer = new char[len];
sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);
CString s = buffer;
....
delete [] buffer;
Where it can be easily written as

CString s;
s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
Note that the sprintf examples are not Unicode-ready (although you could use tsprintf and put _T() around the formatting string, but the basic idea is still that you are doing far more work than is necessary, and it is error-prone.

CString to char * III: Interfacing to a controlA very common operation is to pass a CString value in to a control, for example, a CTreeCtrl. While MFC provides a number of convenient overloads for the operation, but in the most general situation you use the "raw" form of the update, and therefore you need to store a pointer to a string in the TVITEM which is included within the TVINSERTITEMSTRUCT:

TVINSERTITEMSTRUCT tvi;
CString s;
// ... assign something to s
tvi.item.pszText = s; // Compiler yells at you here
// ... other stuff
HTREEITEM ti = c_MyTree.InsertItem(&tvi);
Now why did the compiler complain? It looks like a perfectly good assignment! But in fact if you look at the structure, you will see that the member is declared in the TVITEM structure as shown below:

LPTSTR pszText;
int cchTextMax;
Therefore, the assignment is not assigning to an LPCTSTR and the compiler has no idea how to cast the right hand side of the assignment to an LPTSTR.

OK, you say, I can deal with that, and you write

tvi.item.pszText = (LPCTSTR)s; // compiler still complains!
What the compiler is now complaining about is that you are attempting to assign an LPCTSTR to an LPTSTR, an operation which is forbidden by the rules of C and C++. You may not use this technique to accidentally alias a constant pointer to a non-constant alias so you can violate the assumptions of constancy. If you could, you could potentially confuse the optimizer, which trusts what you tell it when deciding how to optimize your program. For example, if you do

const int i = ...;
//... do lots of stuff
???? ... = a[i];? // usage 1
// ... lots more stuff
???? ... = a[i];? // usage 2
Then the compiler can trust that, because you said const, that the value of i at "usage1" and "usage2" is the same value, and it can even precompute the address of a[i] at usage1 and keep the value around for later use at usage2, rather than computing it each time. If you were able to write

const int i = ...;
int * p = &i;
//... do lots of stuff
???? ... = a[i];? // usage 1
// ... lots more stuff
???? (*p)++;????? // mess over compiler's assumption
// ... and other stuff
???? ... = a[i];? // usage 2
The the compiler would believe in the constancy of i, and consequently the constancy of the location of a[i], and the place where the indirection is done destroys that assumption. Thus, the program would exhibit one behavior when compiled in debug mode (no optimizations) and another behavior when compiled in release mode (full optimization). This Is Not Good. Therefore, the attempt to assign the pointer to i to a modifiable reference is diagnosed by the compiler as being bogus. This is why the (LPCTSTR) cast won't really help.

Why not just declare the member as an LPCTSTR? Because the structure is used both for reading and writing to the control. When you are writing to the control, the text pointer is actually treated as an LPCTSTR but when you are reading from the control you need a writeable string. The structure cannot distinguish its use for input from its use for output.

Therefore, you will often find in my code something that looks like

tvi.item.pszText = (LPTSTR)(LPCTSTR)s;
This casts the CString to an LPCTSTR, thus giving me that address of the string, which I then force to be an LPTSTR so I can assign it. Note that this is valid only if you are using the value as data to a Set or Insert style method! You cannot do this when you are trying to retrieve data!

You need a slightly different method when you are trying to retrieve data, such as the value stored in a control. For example, for a CTreeCtrl using the GetItem method. Here, I want to get the text of the item. I know that the text is no more than MY_LIMIT in size. Therefore, I can write something like

TVITEM tvi;
// ... assorted initialization of other fields of tvi
tvi.pszText = s.GetBuffer(MY_LIMIT);
tvi.cchTextMax = MY_LIMIT;
c_MyTree.GetItem(&tvi);
s.ReleaseBuffer();
Note that the code above works for any type of Set method also, but is not needed because for a Set-type method (including Insert) you are not writing the string. But when you are writing the CString you need to make sure the buffer is writeable. That's what the GetBuffer does. Again, note that once you have done the GetBuffer call, you must not do anything else to the CString until the ReleaseBuffer call.

CString to BSTR?
When programming with ActiveX, you will sometimes need a value represented as a type BSTR. A BSTR is a counted string, a wide-character (Unicode) string on Intel platforms and can contain embedded NUL characters.?

You can convert at CString to a BSTR by calling the CString method AllocSysString:

CString s;
s = ... ; // whatever
BSTR b = s.AllocSysString();
?The pointer b points to a newly-allocated BSTR object which is a copy of the CString, including the terminal NUL character. This may now be passed to whatever interface you are calling that requires a BSTR. Normally, a BSTR is disposed of by the component receiving it. If you should need to dispose of a BSTR, you must use the call

::SysFreeString(b);
to free the string.

The story is that the decision of how to represent strings sent to ActiveX controls resulted in some serious turf wars within Microsoft. The Visual Basic people won, and the string type BSTR (acronym for "Basic String") was the result.

BSTR to CString?
?Since a BSTR is a counted Unicode string, you can use standard conversions to make an 8-bit CString. Actually, this is built-in; there are special constructors for converting ANSI strings to Unicode and vice-versa. You can also get BSTRs as results in a VARIANT type, which is a type returned by various COM and Automation calls.

For example, if you do, in an ANSI application,

BSTR b;
b = ...; // whatever
CString s(b == NULL ? L"" : b)
works just fine for a single-string BSTR, because there is a special constructor that takes an LPCWSTR (which is what a BSTR is) and converts it to an ANSI string. The special test is required because a BSTR could be NULL, and the constructors Don't Play Well with NULL inputs (thanks to Brian Ross for pointing this out!). This also only works for a BSTR that contains only a single string terminated with a NUL; you have to do more work to convert strings that contain multiple NUL characters. Note that embedded NUL characters generally don't work well in CStrings and generally should be avoided.

Remember, according to the rules of C/C++, if you have an LPWSTR it will match a parameter type of LPCWSTR (it doesn't work the other way!).

In UNICODE mode, this is just the constructor

CString::CString(LPCTSTR);
As indicated above, in ANSI mode there is a special constructor for

CString::CString(LPCWSTR);
this calls an internal function to convert the Unicode string to an ANSI string. (In Unicode mode there is a special constructor that takes an LPCSTR, a pointer to an 8-bit ANSI string, and widens it to a Unicode string!). Again, note the limitation imposed by the need to test for a BSTR value which is NULL.

There is an additional problem as pointed out above: BSTRs can contain embedded NUL characters; CString constructors can only handle single NUL characters in a string. This means that CStrings will compute the wrong length for a string which contains embedded NUL bytes. You need to handle this yourself. If you look at the constructors in strcore.cpp, you will see that they all do an lstrlen or equivalent to compute the length.?

Note that the conversion from Unicode to ANSI uses the ::WideCharToMultiByte conversion with specific arguments that you may not like. If you want a different conversion than the default, you have to write your own.

If you are compiling as UNICODE, then it is a simple assignment:

CString convert(BSTR b)
?? {
??? if(b == NULL)
??????? return CString(_T(""));
??? CString s(b); // in UNICODE mode
??? return s;
?? }
If you are in ANSI mode, you need to convert the string in a more complex fashion. This will accomplish it. Note that this code uses the same argument values to ::WideCharToMultiByte that the implicit constructor for CString uses, so you would use this technique only if you wanted to change these parameters to do the conversion in some other fashion, for example, specifying a different default character, a different set of flags, etc.

CString convert(BSTR b)
?? {
??? CString s;
??? if(b == NULL)
?????? return s; // empty for NULL BSTR
#ifdef UNICODE
??? s = b;
#else
??? LPSTR p = s.GetBuffer(SysStringLen(b) + 1);
??? ::WideCharToMultiByte(CP_ACP,??????????? // ANSI Code Page
????????????????????????? 0,???????????????? // no flags
????????????????????????? b,???????????????? // source widechar string
????????????????????????? -1,??????????????? // assume NUL-terminated
????????????????????????? p,???????????????? // target buffer
????????????????????????? SysStringLen(b)+1, // target buffer length
????????????????????????? NULL,????????????? // use system default char
????????????????????????? NULL);???????????? // don't care if default used
??? s.ReleaseBuffer();
#endif
??? return s;
?? }
Note that I do not worry about what happens if the BSTR contains Unicode characters that do not map to the 8-bit character set, because I specify NULL as the last two parameters. This is the sort of thing you might want to change.

VARIANT to CString?
Actually, I've never done this; I don't work in COM/OLE/ActiveX where this is an issue. But I saw a posting by Robert Quirk on the microsoft.public.vc.mfc newsgroup on how to do this, and it seemed silly not to include it in this essay, so here it is, with a bit more explanation and elaboration. Any errors relative to what he wrote are my fault.

A VARIANT is a generic parameter/return type in COM programming. You can write methods that return a type VARIANT, and which type the function returns may (and often does) depend on the input parameters to your method (for example, in Automation, depending on which method you call, IDispatch::Invoke may return (via one of its parameters) a VARIANT which holds a BYTE, a WORD, an float, a double, a date, a BSTR, and about three dozen other types (see the specifications of the VARIANT structure in the MSDN). In the example below, it is assumed that the type is known to be a variant of type BSTR, which means that the value is found in the string referenced by bstrVal.? This takes advantage of the fact that there is a constructor which, in an ANSI application, will convert a value referenced by an LPCWCHAR to a CString (see BSTR-to-CString). In Unicode mode, this turns out to be the normal CString constructor. See the caveats about the default ::WideCharToMultibyte conversion and whether or not you find these acceptable (mostly, you will).

VARIANT vaData;

vaData = m_com.YourMethodHere();
ASSERT(vaData.vt == VT_BSTR);

CString strData(vaData.bstrVal);
Note that you could also make a more generic conversion routine that looked at the vt field. In this case, you might consider something like:

CString VariantToString(VARIANT * va)
?? {
??? CString s;
??? switch(va->vt)
????? { /* vt */
?????? case VT_BSTR:
????????? return CString(vaData->bstrVal);
?????? case VT_BSTR | VT_BYREF:
????????? return CString(*vaData->pbstrVal);
?????? case VT_I4:
????????? s.Format(_T("%d"), va->lVal);
????????? return s;
?????? case VT_I4 | VT_BYREF:
????????? s.Format(_T("%d"), *va->plVal);
?????? case VT_R8:
????????? s.Format(_T("%f"), va->dblVal);
????????? return s;
?????? ... remaining cases left as an Exercise For The Reader
?????? default:
????????? ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
????????? return CString("");
????? } /* vt */
?? }
Loading STRINGTABLE values
If you want to create a program that is easily ported to other languages, you must not include native-language strings in your source code. (For these examples, I'll use English, since that is my native language (aber Ich kann ein bischen Deutsch sprechen). So it is very bad practice to write

CString s = "There is an error";
Instead, you should put all your language-specific strings (except, perhaps, debug strings, which are never in a product deliverable). This means that is fine to write

s.Format(_T("%d - %s"), code, text);
in your program; that literal string is not language-sensitive. However, you must be very careful to not use strings like

// fmt is "Error in %s file %s"
// readorwrite is "reading" or "writing"
s.Format(fmt, readorwrite, filename);
I speak of this from experience. In my first internationalized application I made this error, and in spite of the fact that I know German, and that German word order places the verb at the end of a sentence, I had done this. Our German distributor complained bitterly that he had to come up with truly weird error messages in German to get the format codes to do the right thing. It is much better (and what I do now) to have two strings, one for reading and one for writing, and load the appropriate one, making them string parameter-insensitive, that is, instead of loading the strings "reading" or "writing", load the whole format:

// fmt is "Error in reading file %s"
//????????? "Error in writing file %s"
s.Format(fmt, filename);
Note that if you have more than one substitution, you should make sure that if the word order of the substitutions does not matter, for example, subject-object, subject-verb, or verb-object, in English.

For now, I won't talk about FormatMessage, which actually is better than sprintf/Format, but is poorly integrated into the CString class. It solves this by naming the parameters by their position in the parameter list and allows you to rearrange them in the output string.?

So how do we accomplish all this? By storing the string values in the resource known as the STRINGTABLE in the resource segment. To do this, you must first create the string, using the Visual Studio resource editor. A string is given a string ID, typically starting IDS_. So you have a message, you create the string and call it IDS_READING_FILE and another called IDS_WRITING_FILE. They appear in your .rc file as

STRINGTABLE
??? IDS_READING_FILE "Reading file %s"
??? IDS_WRITING_FILE "Writing file %s"
END
Note: these resources are always stored as Unicode strings, no matter what your program is compiled as. They are even Unicode strings on Win9x platforms, which otherwise have no real grasp of Unicode (but they do for resources!). Then you go to where you had stored the strings?

// previous code
?? CString fmt;
????? if(...)
??????? fmt = "Reading file %s";
???? else
?????? fmt = "Writing file %s";
? ...
??? // much later
? CString s;
? s.Format(fmt, filename);
and instead do

// revised code
??? CString fmt;
??????? if(...)
?????????? fmt.LoadString(IDS_READING_FILE);
??????? else
?????????? fmt.LoadString(DS_WRITING_FILE);
??? ...
????? // much later
??? CString s;
??? s.Format(fmt, filename);
Now your code can be moved to any language. The LoadString method takes a string ID and retrieves the STRINGTABLE? value it represents, and assigns that value to the CString.?

There is a clever feature of the CString constructor that simplifies the use of STRINGTABLE entries. It is not explicitly documented in the CString::CString specification, but is obscurely shown in the example usage of the constructor! (Why this couldn't be part of the formal documentation and has to be shown in an example escapes me!). The feature is that if you cast a STRINGTABLE ID to an LPCTSTR it will implicitly do a LoadString. Thus the following two examples of creating a string value produce the same effect, and the ASSERT will not trigger in debug mode compilations:

CString s;
s.LoadString(IDS_WHATEVER);
CString t( (LPCTSTR)IDS_WHATEVER);
ASSERT(s == t);
Now, you may say, how can this possibly work? How can it tell a valid pointer from a STRINGTABLE ID? Simple: all string IDs are in the range 1..65535. This means that the high-order bits of the pointer will be 0. Sounds good, but what if I have valid data in a low address? Well, the answer is, you can't. The lower 64K of your address space will never, ever, exist. Any attempt to access a value in the address range 0x00000000 through 0x0000FFFF (0..65535) will always and forever give an access fault. These addresses are never, ever valid addresses. Thus a value in that range (other than 0) must necessarily represent a STRINGTABLE ID.

I tend to use the MAKEINTRESOURCE macro to do the casting. I think it makes the code clearer regarding what is going on. It is a standard macro which doesn't have much applicability otherwise in MFC. You may have noted that many methods take either a UINT or an LPCTSTR as parameters, using C++ overloading. This gets us around the ugliness of pure C where the "overloaded" methods (which aren't really overloaded in C) required explicit casts. This is also useful in assigning resource names to various other structures.

CString s;
s.LoadString(IDS_WHATEVER);
CString t( MAKEINTRESOURCE(IDS_WHATEVER));
ASSERT(s == t);
Just to give you an idea: I practice what I preach here. You will rarely if ever find a literal string in my program, other than the occasional debug output messages, and, of course, any language-independent string.

CStrings and temporary objects??
Here's a little problem that came up on the microsoft.public.vc.mfc newsgroup a while ago. I'll simplify it a bit. The basic problem was the programmer wanted to write a string to the Registry. So he wrote:

I am trying to set a registry value using RegSetValueEx() and it is the value that I am having trouble with. If I declare a variable of char[] it works fine. However, I am trying to convert from a CString and I get garbage. "YYYY...YYYYYY" to be exact. I have tried GetBuffer, typecasting to char*, LPCSTR. The return of GetBuffer (from debug) is the correct string but when I assign it to a char* (or LPCSTR) it is garbage. Following is a piece of my code:

char* szName = GetName().GetBuffer(20);
RegSetValueEx(hKey, "Name", 0, REG_SZ,
??????????????????? (CONST BYTE *) szName,
??????????????????? strlen (szName + 1));

The Name string is less then 20 chars long, so I don't think the GetBuffer parameter is to blame.

It is very frustrating and any help is appreciated.

Dear Frustrated,

You have been done in by a fairly subtle error, caused by trying to be a bit too clever. What happened was that you fell victim to knowing too much. The correct code is shown below:

CString Name = GetName();
RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,
??????????????????? (CONST BYTE *) (LPCTSTR)Name,
??????????????????? (Name.GetLength() + 1) * sizeof(TCHAR));

Here's why my code works and yours didn't. When your function GetName returned a CString, it returned a "temporary object". See the C++ Reference manual §12.2.?

In some circumstances it may be necessary or convenient for the compiler to generate a temporary object. Such introduction of temporaries is implementation dependent. When a compiler introduces a temporary object of a class that has a constructor it must ensure that a construct is called for the temporary object. Similarly, the destructor must be called for a temporary object of a class where a destructor is declared.?

The compiler must ensure that a temporary object is destroyed. The exact point of destruction is implementation dependent....This destruction must take place before exit from the scope in which the temporary is created.

Most compilers implement the implicit destructor for a temporary at the next program sequencing point following its creation, that is, for all practical purposes, the next semicolon. Hence the CString existed when the GetBuffer call was made, but was destroyed following the semicolon. (As an aside, there was no reason to provide an argument to GetBuffer, and the code as written is incorrect since there is no ReleaseBuffer performed). So what GetBuffer returned was a pointer to storage for the text of the CString. When the destructor was called at the semicolon, the basic CString object was freed, along with the storage that had been allocated to it. The MFC debug storage allocator then rewrites this freed storage with 0xDD, which is the symbol "Y". By the time you do the write to the Registry, the string contents have been destroyed.

There is no particular reason to need to cast the result to a char * immediately. Storing it as a CString means that a copy of the result is made, so after the temporary CString is destroyed, the string still exists in the variable's CString. The casting at the time of the Registry call is sufficient to get the value of a string which already exists.

In addition, my code is Unicode-ready. The Registry call wants a byte count. Note also that the call lstrlen(Name+1) returns a value that is too small by 2 for an ANSI string, since it doesn't start until the second character of the string. What you meant to write was lstrlen(Name) + 1 (OK, I admit it, I've made the same error!). However, in Unicode, where all characters are two bytes long, we need to cope with this. The Microsoft documentation is surprisingly silent on this point: is the value given for REG_SZ values a byte count or a character count? I'm assuming that their specification of "byte count" means exactly that, and you have to compensate.

CString Efficiency
One problem of CString is that it hides certain inefficiencies from you. On the other hand, it also means that it can implement certain efficiencies. You may be tempted to say of the following code

CString s = SomeCString1;
s += SomeCString2;
s += SomeCString3;
s += ",";
s += SomeCString4;
that it is horribly inefficient compared to, say

char s[1024];
lstrcpy(s, SomeString1);
lstrcat(s, SomeString2);
lstrcat(s, SomeString 3);
lstrcat(s, ",");
lstrcat(s, SomeString4);
After all, you might think, first it allocates a buffer to hold SomeCString1, then copies SomeCString1 to it, then detects it is doing a concatenate, allocates a new buffer large enough to hold the current string plus SomeCString2, copies the contents to the buffer and concatenates the SomeCString2 to it, then discards the first buffer and replaces the pointer with a pointer to the new buffer, then repeats this for each of the strings, being horribly inefficient with all those copies.

The truth is, it probably never copies the source strings (the left side of the +=) for most cases.

In VC++ 6.0, in Release mode, all CString buffers are allocated in predefined quanta. These are defined as 64, 128, 256, and 512 bytes. This means that unless the strings are very long, the creation of the concatenated string is an optimized version of a strcat operation (since it knows the location of the end of the string it doesn't have to search for it, as strcat would; it just does a memcpy to the correct place) plus a recomputation of the length of the string. So it is about as efficient as the clumsier pure-C code, and one whole lot easier to write. And maintain. And understand.?

Those of you who aren't sure this is what is really happening, look in the source code for CString, strcore.cpp, in the mfc/src subdirectory of your vc98 installation. Look for the method ConcatInPlace which is called from all the += operators.

Aha! So CString isn't really "efficient!" For example, if I create

CString cat("Mew!");
then I don't get a nice, tidy little buffer 5 bytes long (4 data bytes plus the terminal NUL). Instead the system wastes all that space by giving me 64 bytes and wasting 59 of them.

If this is how you think, be prepared to reeducate yourself. Somewhere in your career somebody taught you that you always had to use as little space as possible, and this was a Good Thing.

This is incorrect. It ignores some seriously important aspects of reality.?

If you are used to programming embedded applications with 16K EPROMs, you have a particular mindset for doing such allocation. For that application domain, this is healthy. But for writing Windows applications on 500MHz, 256MB machines, it actually works against you, and creates programs that perform far worse than what you would think of as "less efficient" code.

For example, size of strings is thought to be a first-order effect. It is Good to make this small, and Bad to make it large. Nonsense. The effect of precise allocation is that after a few hours of the program running, the heap is cluttered up with little tiny pieces of storage which are useless for anything, but they increase the storage footprint of your application, increase paging traffic, can actually slow down the storage allocator to unacceptable performance levels, and eventually allow your application to grow to consume all of available memory. Storage fragmentation, a second-order or third-order effect, actually dominates system performance. Eventually, it compromises reliability, which is completely unacceptable.

Note that in Debug mode compilations, the allocation is always exact. This helps shake out bugs.

Assume your application is going to run for months at a time. For example, I bring up VC++, Word, PowerPoint, FrontPage, Outlook Express, Forté Agent, Internet Explorer, and a few other applications, and essentially never close them. I've edited using PowerPoint for days on end (on the other hand, if you've had the misfortune to have to use something like Adobe FrameMaker, you begin to appreciate reliability; I've rarely been able to use this application without it? crashing four to six times a day! And always because it has run out of space, usually by filling up my entire massive swap space!) Precise allocation is one of the misfeatures that will compromise reliability and lead to application crashes.

By making CStrings be multiples of some quantum, the memory allocator will end up cluttered with chunks of memory which are almost always immediately reusable for another CString, so the fragmentation is minimized, allocator performance is enhanced, application footprint remains almost as small as possible, and you can run for weeks or months without problem.

Aside: Many years ago, at CMU, we were writing an interactive system. Some studies of the storage allocator showed that it had a tendency to fragment memory badly. Jim Mitchell, now at Sun Microsystems, created a storage allocator that maintained running statistics about allocation size, such as the mean and standard deviation of all allocations. If a chunk of storage would be split into a size that was smaller than the mean minus one s than the prevailing allocation, he didn't split it at all, thus avoiding cluttering up the allocator with pieces too small to be usable. He actually used floating point inside an allocator! His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation. He was right.

Never, ever think about "optimization" in terms of small-and-fast analyzed on a per-line-of-code basis. Optimization should mean small-and-fast analyzed at the complete application level (if you like New Age buzzwords, think of this as the holistic approach to program optimization, a whole lot better than the per-line basis we teach new programmers). At the complete application level, minimum-chunk string allocation is about the worst method you could possibly use.

If you think optimization is something you do at the code-line level, think again. Optimization at this level rarely matters. Read my essay on Optimization: Your Worst Enemy for some thought-provoking ideas on this topic.

Note that the += operator is special-cased; if you were to write:

CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
then each application of the + operator causes a new string to be created and a copy to be done (although it is an optimized version, since the length of the string is known and the inefficiencies of strcat do not come into play).

Summary
These are just some of the techniques for using CString. I use these every day in my programming. CString is not a terribly difficult class to deal with, but generally the MFC materials do not make all of this apparent, leaving you to figure it out on your own.

Acknowledgements
Special thanks to Lynn Wallace for pointing out a syntax error in one of the examples, Brian Ross for his comments on BSTR conversions, and Robert Quirk for his example of VARIANT-to-BSTR conversion.

總結

以上是生活随笔為你收集整理的CString Management (关于CString的所有操作)的全部內容,希望文章能夠幫你解決所遇到的問題。

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

人人爽人人澡人人添人人人人 | 在线免费观看羞羞视频 | 中文字幕日韩国产 | 久久久久女人精品毛片九一 | 夜夜操狠狠干 | 国产精品网站 | 成年人国产在线观看 | 中文字幕在线免费97 | 中文字幕免费观看 | 91精品国产一区 | 99久久www| 国产成人一区在线 | 国产成人精品在线播放 | 久久激情综合网 | 久久免费视频1 | 美女视频黄免费的 | 亚洲视频专区在线 | 国产玖玖视频 | 91精品国产一区二区在线观看 | 99视频导航 | 久久精品婷婷 | 97视频一区 | 国产成人91 | 国产黄色资源 | 日韩欧美久久 | 亚洲欧洲国产日韩精品 | 99re6热在线精品视频 | 91色综合 | 亚洲三级在线免费观看 | 久久综合九色99 | 久久在线视频在线 | 又黄又爽又无遮挡免费的网站 | 精品亚洲午夜久久久久91 | 一区二区三区在线观看免费视频 | 99精品热视频只有精品10 | 婷婷激情欧美 | 欧美日韩国产综合网 | 国产美女网站在线观看 | 在线观看中文字幕网站 | 欧美 高跟鞋交 xxxxhd | 在线免费av播放 | 亚洲激情视频 | 91欧美在线| 日韩精品一区二区三区免费观看视频 | 成人免费视频免费观看 | 精品免费视频123区 午夜久久成人 | 国产一区高清在线观看 | 亚洲精品乱码久久久久久写真 | 日一日干一干 | 天天爱天天操天天干 | 91麻豆精品国产91久久久使用方法 | 欧美最爽乱淫视频播放 | av中文字幕不卡 | 国产精品一区在线观看 | 欧美坐爱视频 | 国产日产欧美在线观看 | 国内精品久久久久久久久久 | 国产精品99久久久久久宅男 | 亚洲高清精品在线 | 国产99久久精品 | 欧美精品久久久久久久久久久 | 亚洲少妇xxxx | a级片在线播放 | 日本超碰在线 | 日本高清中文字幕有码在线 | 精品影院一区二区久久久 | 国产日韩精品久久 | 国产又粗又猛又黄 | 久久久精品免费观看 | 亚洲爽爽网 | 在线观看亚洲国产 | 久久r精品 | 亚洲精品久久激情国产片 | www91在线观看 | 麻豆一级视频 | avcom在线| 国产在线 一区二区三区 | 国产黄色精品视频 | 色综合亚洲精品激情狠狠 | 午夜视频在线观看网站 | 国产精品免费在线观看视频 | www夜夜操com | 亚洲视频精品在线 | 中文字幕乱码亚洲精品一区 | 中文乱幕日产无线码1区 | 婷婷六月久久 | 最新国产精品亚洲 | 色视频在线看 | 亚洲好视频 | 91成品人影院 | 久草在线观看 | 五月天激情视频 | 午夜精品三区 | 亚洲国产精彩中文乱码av | 91福利视频久久久久 | 欧美日韩久久不卡 | 婷婷久久国产 | 日韩性久久 | 久久久精品成人 | 九九九九热精品免费视频点播观看 | 色在线网 | 久久经典视频 | 射射射av | 91精品婷婷国产综合久久蝌蚪 | 韩国在线视频一区 | 不卡的一区二区三区 | 国产黄色精品在线观看 | 97视频免费在线观看 | 色视频网站免费观看 | 99久久精品无码一区二区毛片 | a天堂一码二码专区 | 亚洲视频999| 日韩成人精品 | 久草久热 | 中文字幕文字幕一区二区 | 亚洲乱码在线观看 | 国产精品69久久久久 | 91视频在线观看免费 | av电影中文| 婷婷久草 | 国产伦精品一区二区三区在线 | 精品国产一区二区三区久久久久久 | 黄色av一区 | 青青色影院 | 久草精品视频在线看网站免费 | 欧美日韩一区二区视频在线观看 | 免费国产亚洲视频 | 日韩二区在线 | 中文资源在线官网 | 99久久久久久久 | 亚洲欧洲一区二区在线观看 | 久久艹在线 | 久久精品3| 天天操天天色综合 | 国产高清免费在线观看 | 国产尤物视频在线 | 久久高清国产 | 久久热首页 | 91热视频 | 亚洲欧美日本国产 | 久精品视频 | 91精品久久久久久综合乱菊 | 中文字幕免费不卡视频 | 色婷婷国产精品一区在线观看 | 麻豆视传媒官网免费观看 | 久久综合成人网 | 国产视频2区 | 黄网站app在线观看免费视频 | 国产欧美日韩视频 | 美女网站视频免费黄 | 亚洲免费专区 | 国产精品美女久久久久久久 | 亚洲成人av在线播放 | 国产偷v国产偷∨精品视频 在线草 | 久久国产精品免费 | 国产精品二区三区 | 欧美黑人猛交 | av电影免费在线播放 | 中文字幕在线播放一区 | 婷婷婷国产在线视频 | 免费婷婷 | 日韩色在线观看 | 福利一区二区三区四区 | www黄| 免费网站看av片 | 色偷偷88欧美精品久久久 | 成人小视频在线观看免费 | 日本中文字幕在线看 | 99亚洲天堂 | 2022中文字幕在线观看 | 六月丁香激情综合 | 日韩黄色免费看 | 亚洲视频免费视频 | 国产午夜精品一区 | 国产一级做a| 成人av在线亚洲 | 在线成人性视频 | 五月婷婷六月丁香在线观看 | 亚洲专区欧美 | 欧美午夜精品久久久久 | 高清av中文在线字幕观看1 | 久久福利国产 | 亚洲午夜电影网 | 久久精品毛片 | 国产又黄又爽又猛视频日本 | 超碰97在线资源站 | 在线中文字幕电影 | 在线免费观看视频 | 久草在线 | 日韩精品最新在线观看 | 国产高清在线 | 国产在线日本 | 亚洲国产中文在线观看 | 欧美日韩一区二区三区免费视频 | 人人天天夜夜 | 中文在线免费观看 | 国产亚洲日 | 888av| 国产精品成人一区二区 | 免费a级毛片在线看 | 综合色中色 | 亚洲国产一区二区精品专区 | 久久黄色网页 | 色99导航| 99精品视频免费看 | 毛片3 | 黄色小网站在线 | 免费精品在线视频 | 97国产在线 | 日韩videos高潮hd | 国产精品麻豆果冻传媒在线播放 | 国产一区二区三区高清播放 | 深爱开心激情 | 91精品人成在线观看 | 久久久久久片 | 国产福利一区在线观看 | 国产中文字幕久久 | 久久久久久久综合色一本 | 中文成人字幕 | 亚洲情感电影大片 | 欧美性生活一级片 | 久久午夜色播影院免费高清 | 丁香电影小说免费视频观看 | 超碰97在线资源站 | 亚洲第一区在线观看 | 日韩亚洲欧美中文字幕 | 在线 国产 亚洲 欧美 | 免费电影一区二区三区 | 国产黄色a| 国产一区二区高清 | 国产精品久久久区三区天天噜 | 精品国产一区二区三区四 | 中文字幕欲求不满 | 成 人 黄 色 视频 免费观看 | 人人添人人澡 | 久久激情五月丁香伊人 | 久国产在线播放 | 天天天天天天天天操 | 精品一区二三区 | 久久综合偷偷噜噜噜色 | 婷婷国产视频 | 99久精品视频| 中文在线亚洲 | 国产又黄又硬又爽 | 亚洲视频aaa | 东方av在线免费观看 | 欧美一级片在线播放 | 欧美一级久久久 | 日躁夜躁狠狠躁2001 | 一区二区三区在线观看免费视频 | 在线观看日本韩国电影 | 亚洲精品乱码久久久久久高潮 | 久久久免费观看完整版 | av高清免费在线 | 伊人色综合久久天天 | 亚洲精品资源 | 婷婷久久综合九色综合 | 日韩欧美在线观看一区二区三区 | 美女视频是黄的免费观看 | 97成人精品视频在线播放 | 一区二区三区手机在线观看 | 国产91精品一区二区麻豆亚洲 | 国产精品久久久久久一区二区三区 | 亚洲成人黄色在线 | 超薄丝袜一二三区 | 在线观看成人 | 狠狠色婷婷丁香六月 | 久草在线视频新 | 9ⅰ精品久久久久久久久中文字幕 | 免费看久久久 | 精品欧美一区二区三区久久久 | 久久久久草| 色av资源网| 在线日本看片免费人成视久网 | 久久不卡电影 | 日本中出在线观看 | 国产视频九色蝌蚪 | 美腿丝袜一区二区三区 | 在线观看久 | 色综合天天狠天天透天天伊人 | 日本韩国在线不卡 | 91免费版成人 | 国产日韩精品在线观看 | 中文字幕在线视频精品 | 亚洲精品视频在线 | 99re国产 | 国产精品第72页 | 91视频这里只有精品 | 日韩欧美一区二区在线观看 | 成人在线观看网址 | 一级成人在线 | 日本aaa在线观看 | 在线网站黄 | 成人免费在线电影 | 亚洲精品视频大全 | 欧美不卡在线 | 久草在线视频中文 | 天天操天天干天天操天天干 | 在线免费观看视频你懂的 | 不卡中文字幕在线 | 国产精品美女久久久久久免费 | 国产一区二区精品 | 亚洲女人av | 狠狠色丁香婷婷综合基地 | 18国产精品白浆在线观看免费 | 久久线视频 | 高清一区二区三区av | 日韩区欧美久久久无人区 | 在线观看中文字幕一区 | 天天操天天干天天操天天干 | 久久只精品99品免费久23小说 | 色综合天天做天天爱 | 国产美女精品久久久 | 免费观看福利视频 | 一区二区三区av在线 | 深爱激情五月网 | 日韩国产精品毛片 | 亚洲在线网址 | 久久久久久久毛片 | 久久久精选| 9999亚洲| 黄色成人在线 | avav片 | 韩国精品在线 | 成人超碰在线 | 亚洲精品国产精品久久99 | 伊人久久在线观看 | 国产一区二区精品久久 | 色诱亚洲精品久久久久久 | 在线日韩中文字幕 | 伊人影院在线观看 | 久久久久久久18 | 欧美日视频| 成年人精品| 国产精品美女免费视频 | 国产成人精品久久久久 | 国产成人一区二区三区在线观看 | 成人免费观看视频网站 | 久久久久99精品成人片三人毛片 | 五月综合| 一区二区三区日韩在线观看 | 国产精品欧美久久久久三级 | 成人丁香花 | 亚洲精品综合在线观看 | 精品一区二区三区在线播放 | 国产黄色播放 | 国内精品久久久久久久久久 | 日韩一区二区久久 | 色视频成人在线观看免 | 国产精品精品国产色婷婷 | 99视频偷窥在线精品国自产拍 | 国产色在线 | 中文av字幕在线观看 | 在线电影91 | 在线观看国产福利片 | 国产玖玖视频 | 97精品国自产拍在线观看 | 色999五月色 | 中文字幕色在线视频 | 91在线精品秘密一区二区 | 久久精品亚洲综合专区 | 久草视频免费 | 久久免费视屏 | 午夜男人影院 | 中文字幕在线看 | 在线观看国产永久免费视频 | 黄色免费大全 | 精品久久免费看 | 久草青青在线观看 | 免费观看www小视频的软件 | 最新av中文字幕 | 久久久久久久久久久成人 | av资源网在线播放 | www亚洲视频 | 国产又粗又猛又黄 | 国产精品久久久久久爽爽爽 | 在线亚洲播放 | 国产精品18久久久久久不卡孕妇 | 在线观看中文字幕2021 | 久久五月天综合 | 一区二区欧美日韩 | www.狠狠干 | 一级片免费观看 | av网站地址 | 国产一级二级三级视频 | 最近中文字幕大全 | 91九色porn在线资源 | 99激情网 | 中文字幕免费观看视频 | 久久艹欧美 | 4438全国亚洲精品观看视频 | 精品成人在线 | 1000部18岁以下禁看视频 | 国产精品欧美日韩在线观看 | 公开超碰在线 | 国产日本在线 | 国产女人40精品一区毛片视频 | 国产精品美女久久久久久久久 | 91成人短视频在线观看 | 日韩一区二区三区观看 | 日韩在线一二三区 | 狠狠久久婷婷 | 国产色在线观看 | 69久久久久久久 | 国产精品久久麻豆 | 久久久免费视频播放 | 99精品在线免费观看 | 狠狠网| 国产精品久久伊人 | 97福利 | 色七七亚洲影院 | 中文字幕在线影院 | 91麻豆精品一区二区三区 | 国产中文字幕在线看 | 成年人黄色大片在线 | 狠狠狠色 | 久久精品波多野结衣 | 91看片在线观看 | 丁香婷婷激情国产高清秒播 | 日韩在线免费视频观看 | 丁香婷婷色综合亚洲电影 | 在线观看视频91 | 色狠狠久久av五月综合 | 午夜久久福利视频 | 国产精品a成v人在线播放 | 中文字幕最新精品 | 亚洲成人动漫在线观看 | 欧美日韩调教 | 美女久久久久久久久久久 | 99色视频在线 | 亚洲三级网 | 日韩欧美在线中文字幕 | 高清av在线免费观看 | 国产日本亚洲 | 激情视频区 | 国产小视频91 | 黄在线免费观看 | 成人在线超碰 | 最近中文字幕在线播放 | 国产麻豆视频免费观看 | 亚洲日韩中文字幕在线播放 | 四虎成人免费观看 | 精品免费视频 | 少妇性色午夜淫片aaaze | 亚洲码国产日韩欧美高潮在线播放 | 国产 视频 高清 免费 | 亚洲精品在线免费观看视频 | 日韩激情在线视频 | 久久视频网址 | 91麻豆国产福利在线观看 | 国产福利精品一区二区 | 日韩在线视频精品 | 中文字幕第一页在线播放 | 91亚洲精品乱码久久久久久蜜桃 | 国产精品第一视频 | wwwwwww色| 久久综合久久综合九色 | 久久精品国产成人 | 欧美亚洲国产精品久久高清浪潮 | 亚州欧美精品 | 日韩经典一区二区三区 | 久久这里只有精品首页 | 中文视频在线播放 | 亚洲理论在线观看电影 | 亚洲电影第一页av | 国产精品美女 | 婷婷在线不卡 | 狠狠色丁香婷婷综合久小说久 | 欧美最猛性xxxxx免费 | 99人久久精品视频最新地址 | 国产一区二区三区免费在线 | 欧美日韩视频网站 | 国产资源在线观看 | 国产精品视频999 | 国产成人三级一区二区在线观看一 | 国产精品不卡在线播放 | 欧美精品第一 | 国产视频 亚洲视频 | 久久久久女教师免费一区 | 狠狠干天天射 | 97超碰精品 | 久久久久久国产精品亚洲78 | 日韩欧美在线中文字幕 | 午夜久久网| 亚洲区精品视频 | 男女拍拍免费视频 | 天天干天天综合 | 国产视频精品网 | 天天操夜夜想 | 久久老司机精品视频 | 9999精品视频 | 日日夜夜网 | 四川bbb搡bbb爽爽视频 | 亚洲国产精品va在线看黑人动漫 | 天天爽天天碰狠狠添 | 国产精品精品国产婷婷这里av | 在线亚洲精品 | 天天射,天天干 | 98福利在线 | 人人看97| 成人一级影视 | 国产视频2区 | 久久精品综合 | 久视频在线播放 | 青青久视频 | 中文字幕999| 免费观看一区二区三区视频 | 久热av| 国产不卡在线播放 | 国产男女无遮挡猛进猛出在线观看 | 天天爱综合 | 国产精品欧美久久久久无广告 | 精品一区免费 | 国产123区在线观看 国产精品麻豆91 | 久久视影 | 免费观看一区二区三区视频 | 99免在线观看免费视频高清 | 97手机电影网 | 一区二区三区视频网站 | 又爽又黄又刺激的视频 | 伊人电影在线观看 | 夜夜骑天天操 | 最新国产视频 | 国产免码va在线观看免费 | 丝袜制服综合网 | 日日夜日日干 | 日韩精品大片 | 干天天 | 欧美日韩免费网站 | 奇米先锋| 福利电影一区二区 | 亚洲专区免费观看 | 欧美-第1页-屁屁影院 | 国产精品视频全国免费观看 | 玖玖玖在线观看 | 国产精品激情在线观看 | 精品99久久| 日本黄色免费网站 | 久久久久9999亚洲精品 | 午夜免费视频网站 | 成人在线视频免费观看 | 免费观看丰满少妇做爰 | 91精品专区 | 亚洲精品在线观看免费 | 日本精品在线视频 | www.99热精品 | 91视频免费播放 | 久久久国产精品免费 | 91看片在线看片 | 国产精品久久久久久久av大片 | 精品国产欧美一区二区三区不卡 | 热久久国产精品 | 韩日精品在线 | 欧美日韩精品免费观看 | 成人xxxx| 国产91亚洲精品 | 99精品在线免费 | 特级黄录像视频 | 日韩中文在线观看 | 欧美激情第十页 | 最近中文字幕完整视频高清1 | 91在线产啪 | 97国产情侣爱久久免费观看 | 日韩免费在线网站 | 欧美少妇xxx| 日韩高清成人在线 | 久久久久国产精品免费免费搜索 | 在线观看视频国产 | 狠狠干夜夜操 | 亚洲高清视频在线播放 | 亚洲手机天堂 | 久久男人免费视频 | 午夜色婷婷 | 狠狠干狠狠久久 | 日韩av成人免费看 | 在线视频欧美精品 | 免费精品视频在线观看 | www.日日操.com| 激情综合网婷婷 | 婷婷综合影院 | 国产aa免费视频 | 天天色综合三 | 久久久久中文字幕 | 91激情视频在线播放 | 欧美另类一二三四区 | 国产精品免费在线 | 一区二区三区四区精品视频 | 91黄色在线看| 黄色毛片电影 | 在线有码中文字幕 | 国产一区黄色 | 黄色av电影在线观看 | 有码视频在线观看 | 中文av在线播放 | av在线色| 看毛片的网址 | 久久久免费高清视频 | 激情大尺度视频 | 狠狠操狠狠 | av免费在线免费观看 | 国产91成人 | 免费看av片网站 | 99久久www | 色中射| 97成人在线观看 | 中文字幕影片免费在线观看 | 国产精品精品久久久 | www.色com | 国产精品一区二区电影 | 人人干天天干 | 国产不卡高清 | 欧美一级性生活片 | 国产精品观看在线亚洲人成网 | 国产精品久久久久免费观看 | 99久久99热这里只有精品 | 91大神在线看 | 狠狠干成人 | 99久久久国产精品 | 波多野结衣视频一区二区三区 | 久久99久久精品 | 狠狠色丁香久久婷婷综合_中 | 欧美日韩国产在线 | 日本大尺码专区mv | 天天综合久久 | 国产午夜三级一区二区三桃花影视 | 一级精品视频在线观看宜春院 | 一区二区三区在线播放 | 精品在线视频播放 | 日本黄色免费播放 | 成人中文字幕+乱码+中文字幕 | 久久激情电影 | 国产亚洲精品久久久久久久久久久久 | 操操操综合 | 99婷婷狠狠成为人免费视频 | 97人人澡人人爽人人模亚洲 | 香蕉视频网站在线观看 | 人人插人人做 | 国产一级电影免费观看 | 欧美国产不卡 | 久久免费视频一区 | www.成人sex | 99在线视频网站 | 日韩成人黄色av | 青青五月天 | 99精品黄色片免费大全 | 久久av高清 | 久草在线欧美 | 91成人精品| 欧美a√在线 | 亚洲欧美日韩国产一区二区 | 最近久乱中文字幕 | 91麻豆传媒| 国产伦精品一区二区三区免费 | 一级黄色在线免费观看 | 久久av免费| 99视频在线 | 日韩三级视频在线观看 | 在线黄色毛片 | 99热国产在线 | 亚洲精品久久视频 | 黄色资源网站 | 国产视频一区精品 | 啪嗒啪嗒免费观看完整版 | www.99热精品 | 日b视频在线观看网址 | 麻豆视频国产在线观看 | 国产精品一区二区在线播放 | 国产美女主播精品一区二区三区 | 国产免费作爱视频 | 亚洲精品乱码久久久久久蜜桃欧美 | 亚洲成人av电影在线 | 免费看短| 久久婷婷开心 | 91禁看片| 亚洲天堂网视频在线观看 | 色天堂在线视频 | 免费看三级黄色片 | 免费黄色a网站 | 久久婷婷网 | 狠狠狠色丁香婷婷综合激情 | 成人在线播放网站 | 国产成人av电影 | 亚洲精品色婷婷 | 成人91在线 | 91黄色免费网站 | 亚洲丝袜中文 | 久久久久亚洲精品国产 | 黄污视频网站大全 | 色噜噜狠狠狠狠色综合 | 亚洲丁香久久久 | 国产精品系列在线 | 国产精品久久久久久久久久免费看 | 婷婷丁香狠狠爱 | 国产精品国产三级国产 | 玖玖色在线观看 | 日韩在线小视频 | av福利第一导航 | 毛片基地黄久久久久久天堂 | 中文久久精品 | www婷婷 | 免费在线观看av电影 | 91视频免费播放 | 在线观看国产区 | 伊人永久在线 | 国产精品美女久久 | 免费视频一级片 | 免费看精品久久片 | 久久国语| 91精品国产欧美一区二区成人 | 色视频在线观看 | 高清av在线免费观看 | 国产 视频 高清 免费 | 久久成 | 超碰97人 | 国产午夜精品一区二区三区在线观看 | 免费av电影网站 | 色国产精品 | 丁香视频全集免费观看 | 色999精品| 国产一级三级 | www.黄色在线| 久久五月激情 | 日韩三级免费观看 | 国产91成人 | 特级aaa毛片| 国产成人精品在线 | 一区 二区 精品 | 天天操天天干天天综合网 | 超碰在线日韩 | 特级xxxxx欧美 | 久久久久一区二区三区四区 | 97国产精品一区二区 | 91av视频免费观看 | 精品国产自在精品国产精野外直播 | 超碰97成人| 一本一道波多野毛片中文在线 | 六月丁香激情综合色啪小说 | 亚洲h色精品 | 色干干 | 久草色在线观看 | 最新午夜 | 国产91精品久久久久 | 国产91综合一区在线观看 | 久久久久久久久久久久99 | 国产黄色免费观看 | av高清一区二区三区 | 国产中出在线观看 | 97人人射| 在线免费高清 | 国产精品一区在线播放 | 久久精品第一页 | 91自拍视频在线观看 | 久久久精品高清 | 国产视频资源在线观看 | 精品资源在线 | 91污视频在线观看 | 在线中文字幕视频 | 日韩欧美精品在线视频 | 国产特级毛片 | 国产99在线 | 久久资源总站 | 9999国产精品 | 六月激情网 | 国产精品久久久久久一区二区 | 99久久er热在这里只有精品66 | 久久精品国产免费观看 | 激情婷婷综合 | 日韩特级毛片 | 亚洲精品一区中文字幕乱码 | aa一级片 | 香蕉精品视频在线观看 | 久久五月情影视 | 成人毛片100免费观看 | 九九九九九九精品任你躁 | 五月黄色| av免费观看高清 | 成人羞羞视频在线观看免费 | 中文字幕一区二区三区四区久久 | 99久久久国产精品美女 | 国产亚洲小视频 | 精品999久久久 | 久久综合久久鬼 | 国产精品一二 | 国产高清成人 | 97精品久久 | 国产精品美女久久久久久久久久久 | 久久精彩视频 | 在线成人一区 | 日韩欧美在线综合网 | 国产免费亚洲高清 | 国产精品久久久久aaaa九色 | 午夜色影院| av短片在线观看 | 久久一区二区三区日韩 | 91香蕉视频黄色 | 亚洲最大av | a√天堂资源 | 91日韩在线专区 | 黄色小网站免费看 | 久久tv| 国产成人精品国内自产拍免费看 | 18国产精品福利片久久婷 | 天堂网一区二区三区 | 欧美性脚交 | 国产精品久久久久一区二区三区共 | 97视频免费观看 | 国产h片在线观看 | 亚洲区另类春色综合小说校园片 | 91插插插免费视频 | 中文字幕精品www乱入免费视频 | 久久久久久免费网 | 17婷婷久久www | 久久免费精彩视频 | 亚洲午夜精品在线观看 | www.夜夜爱 | 精品久久久99 | 国产精品va最新国产精品视频 | 色噜噜狠狠狠狠色综合 | 一本一本久久a久久精品综合妖精 | 欧美 高跟鞋交 xxxxhd | 97热在线观看| 成人在线一区二区 | 久久免费视频3 | 五月婷婷丁香激情 | 欧美,日韩 | www久久 | 亚洲一区二区观看 | 99亚洲天堂| 91亚洲在线 | 在线观看日韩免费视频 | 久久国产福利 | 欧美一级性 | 日韩,中文字幕 | 久久99久国产精品黄毛片入口 | 五月天婷亚洲天综合网精品偷 | 人人舔人人插 | 日韩久久精品一区二区 | 中文字幕在线视频一区二区 | 成人禁用看黄a在线 | 中文字幕一区二区三区视频 | 国产破处精品 | 激情五月综合 | 成人免费一区二区三区在线观看 | www.亚洲视频.com | 黄色官网在线观看 | 99这里只有精品视频 | se视频网址 | 在线视频免费观看 | 手机版av在线 | 中文字幕一区二区三区在线视频 | 公开超碰在线 | 精品自拍av | 2019av在线视频| 精品99久久 | 国产区在线看 | 在线免费视频 你懂得 | 国产无区一区二区三麻豆 | 欧美污污视频 | 极品美女被弄高潮视频网站 | 国产成人高清 | 天天射,天天干 | 五月天亚洲激情 | 欧美在一区 | 六月婷婷久香在线视频 | 久久久久久久久久久精 | 国产精品女人网站 | 国产激情小视频在线观看 | 国产精品久久免费看 | 日韩动漫免费观看高清完整版在线观看 | 欧美精品久 | 久久久午夜电影 | 久久人人爽人人人人片 | 国产精品乱码在线 | 国产精品久久久久av福利动漫 | 亚洲午夜精品一区二区三区电影院 | 欧美日韩国产高清视频 | 久久国产美女 | 日韩欧美一区视频 | 天天干天天做 | 国产一区在线免费观看视频 | 国产96在线观看 | 欧美色精品天天在线观看视频 | 成人app在线免费观看 | 国产精品视频在线看 | 黄色成人在线 | 欧美日韩一区二区三区在线观看视频 | 99精品欧美一区二区三区黑人哦 | av再线观看| 精品女同一区二区三区在线观看 | 成人香蕉视频 | 天天干天天做 | 91精品在线播放 | 免费日韩 精品中文字幕视频在线 | 97激情影院| 欧美激情另类 | 啪啪午夜免费 | 天天干天天操天天做 | 992tv在线观看 | 2021国产视频 | 国产精品色在线 | 夜夜夜夜夜夜操 | 在线不卡中文字幕播放 | 美女黄网站视频免费 | 人人看人人爱 | 五月婷婷,六月丁香 | 久草视频一区 | 五月婷婷精品 | av黄色影院 | 国产乱码精品一区二区三区介绍 | 久久男人免费视频 | 久久精品视频国产 | 国产69精品久久久久久久久久 | 天天干天天干天天射 | 国产高清无线码2021 | 国产日韩精品一区二区在线观看播放 | 亚洲黄色av一区 | 91 在线视频播放 | 亚洲精品国产第一综合99久久 | 欧洲精品码一区二区三区免费看 | 天堂网一区二区三区 | 亚洲高清在线观看视频 | 欧亚久久 | 伊人婷婷久久 | 在线观看日韩一区 | 欧美日韩亚洲一 | 高清在线观看av | 欧美 日韩 久久 | 97香蕉久久国产在线观看 | 操操综合 | 日本亚洲国产 | 亚洲电影第一页av | 91网站观看 | 日本黄色大片儿 | 日韩精品一区二区三区在线播放 | www.69xx | 国产免费观看高清完整版 | 久久激情综合网 | 国产伦精品一区二区三区照片91 | 99免费在线播放99久久免费 | 国产亚洲欧美日韩高清 | 亚洲综合干| 天天操操操操操操 | 成人av在线资源 | 99视频国产精品免费观看 | 操操操天天操 | 韩国一区二区三区视频 | 亚洲永久av | 波多野结衣综合网 | 久久99精品一区二区三区三区 | 国产精品igao视频网入口 | 国产麻豆精品一区二区 | 天堂资源在线观看视频 | www.狠狠插.com | 99色国产 | 日韩极品视频在线观看 | 欧美一级欧美一级 | 天天综合区 | 亚洲精品在线观看免费 | 69视频在线播放 | 久久ww| 成年人视频在线 | 欧美精品一级视频 | 久久短视频 | 99久久99久久 | 狠狠狠色丁香婷婷综合久久88 | 成人a v视频 | 免费观看国产成人 | 精品福利在线视频 | 亚洲国产成人在线播放 | 国产一级性生活 | 久草在线资源观看 | 久草在线最新 | 五月婷婷.com | 女人高潮特级毛片 | 国产亚洲aⅴaaaaaa毛片 | 亚洲精品美女视频 | 国产高清中文字幕 | 国产精品av免费在线观看 | 免费色视频网站 | 成人在线免费视频观看 | 中文字幕在线观看2018 | 中文字幕九九 | 天天干天天摸 | 国产精品久久99 | 韩日精品中文字幕 | 国产精品嫩草影院99网站 | 亚洲精品女人 | 亚洲午夜精品久久久久久久久 | 草久久影院 | 久久久亚洲麻豆日韩精品一区三区 | 啪啪小视频网站 | 国产精品1区2区 | 中文字幕在线观看1 | 久久久亚洲国产精品麻豆综合天堂 | 欧美特一级| 国产 欧美 日韩 | 91综合色| 在线观看中文字幕一区 |