A couple of questions re CString -vs- std::string ...
1. What are the pros and cons of using CString -vs- std::string ?
2. If you were going to pick one to be your primary string type til the end of time, which would you pick?
Thanks
Printable View
A couple of questions re CString -vs- std::string ...
1. What are the pros and cons of using CString -vs- std::string ?
2. If you were going to pick one to be your primary string type til the end of time, which would you pick?
Thanks
std::string is part of the C++ Standard so, IMO, that is the best choice
And please do a search.. There have been oodles of threads about this in the past.
Never take something with you for ever. New things often appear and you should always use what is the best. There are pro and cons for the string but just do a research on the forum. As for myself, when I am doing a MFC project a use CString.Quote:
Originally Posted by rlewkov
If you're using MFC, CString is a far better choice over std::string.
IMHO, CString is the only MFC class that is better then the STL version.
The std::string that comes with VC++ 6.0/7.x is very inefficient compare to CString.
std::string peforms very badly compare to CString.
Also IMHO, CString has a better string interface then std::string does.
CString is also easier to code when you want to be able to compile a project to either UNICODE or ANSI.
CString has an operator LPCTSTR(), so you don't have to explicitly cast.
With std::string, you have to use the c_str() member function to pass the pointer.
This point can also be consider a CON, but IMHO, the benifit far aways the down side.
CString has a GetBuffer() function which allows you to modify the buffer directly.
According to the C++ standard there's no portable way to modify the std::string buffer directly.
CString has a CompareNoCase function so you can make case insensitive comparisons.
CString works easier with many MFC and Windows API functions.
The major CON with CString is that is not portable. However, there are some CString versions on the net that are portable.
Well...I do not want to get into all of the above since our view is very different from each other in this case, however....the above is not really true...there is of course a portable way...using a 'vector' as a temporary buffer... :cool:Quote:
Originally Posted by Axter
My comment is true.Quote:
Originally Posted by Andreas Masur
Even if you use vector as a temporary buffer, you're still not modifying the buffer directly.
There are many ways to modify the std::string buffer indirectly. But my comment reffers to direct modification.
You have to show this to us in terms of real code. Badly doing what operations?Quote:
Originally Posted by Axter
Maybe MFC functions that require a CString, but I don't see how a Windows API function would be easier with CString as opposed to std::string.Quote:
CString works easier with many MFC and Windows API functions.
And many of those versions actually wrap std::string with calls that mimic CString.Quote:
The major CON with CString is that is not portable. However, there are some CString versions on the net that are portable.
The one disadvantage to CString that has not been pointed out is that std::string handles binary data easily with no tricks. I don't know if CString can handle binary data (for example, appending binary data on a CString that may contain embedded NULLs). In that respect, std::string has the functions to handle binary data (i.e, append(), data(), assign(), etc.), that AFAIK do not exist in CString.Quote:
Also IMHO, CString has a better string interface then std::string does
Regards,
Paul McKenzie
If you run the code below, you'll see the peformance difference between CString and std::string.Quote:
Originally Posted by Paul McKenzie
Run the test in release mode (not debug).
The code below will show that CString out peformance std::string by a factor of MORE then 10 to 1.
Compile in VC++ 6.0 using std::string that comes with VC++.
Code:int dummy_var = 0;
void function1(const char*Data)
{
CString strData = Data;
dummy_var += strData[99];
}
void function2(const char*Data)
{
std::string strData = Data;
dummy_var += strData[99];
}
void function3(const char*Data)
{
CString strData;
strData = Data;
dummy_var += strData[99];
}
void function4(const char*Data)
{
std::string strData(513, 0);
strData = Data;
dummy_var += strData[99];
}
void function5(const char*Data)
{
std::string strData(999, 0);
strData = Data;
dummy_var += strData[99];
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const QtyTimesTest = 99999;
DWORD StartTime, LenTime1=0, LenTime2=0, LenTime3=0, LenTime4=0, LenTime5=0;
const int SizeData = 512;
char DummyData[SizeData+1];
memset(DummyData, 'x', SizeData);
DummyData[SizeData] = 0;
{
StartTime = GetTickCount();
for (int i = 0;i < QtyTimesTest;++i)
{
function1(DummyData);
}
LenTime1 += (GetTickCount() - StartTime);
}
{
StartTime = GetTickCount();
for (int i = 0;i < QtyTimesTest;++i)
{
function2(DummyData);
}
LenTime2 += (GetTickCount() - StartTime);
}
{
StartTime = GetTickCount();
for (int i = 0;i < QtyTimesTest;++i)
{
function3(DummyData);
}
LenTime3 += (GetTickCount() - StartTime);
}
{
StartTime = GetTickCount();
for (int i = 0;i < QtyTimesTest;++i)
{
function4(DummyData);
}
LenTime4 += (GetTickCount() - StartTime);
}
{
StartTime = GetTickCount();
for (int i = 0;i < QtyTimesTest;++i)
{
function5(DummyData);
}
LenTime5 += (GetTickCount() - StartTime);
}
printf("Time duration is as follow:\n\
LenTime1 = %li\n\
LenTime2 = %li\n\
LenTime3 = %li\n\
LenTime4 = %li\n\
LenTime5 = %li\n",
LenTime1, LenTime2, LenTime3, LenTime4, LenTime5);
if (!dummy_var) printf("dummy %i", dummy_var);
system("pause");
Example:Quote:
Originally Posted by Paul McKenzie
int MessageBox(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
The CString variable is handed over to the API function directly, without having to call a member function. Moreover, it compiles on UNICODE and ANSI projects without having to modify the code, or create another compiler directive.Code:CString CstrData = _T("Hello");
std::string stdStrData = "Hello";
MessageBox(NULL, CstrData, NULL, 0); //Compiles on UNICODE and ANSI
MessageBox(NULL, stdStrData.c_str(), NULL, 0); //Only compiles on ANSI (need wstring for UNICODE)
I agree.Quote:
Originally Posted by Paul McKenzie
CString can also handle binary data.Quote:
Originally Posted by Paul McKenzie
Example code:
More over, the GetBuffer() function can be used similar to the data() function of std::string.Code:char data1[] = "\0\1\2\3\4\5\6\7 Hello World";
char data2[] = "\0\7\6\5\4\3\2\1";
CString binData(data1, sizeof(data1)-1);
binData += CString(data2, sizeof(data2)-1);
for (int i = 0;i < binData.GetLength();++i)
{
cout << (int)binData[i] << endl;
}
system("pause");
I agree that not having an assign() nor an append() function is a limitation on the CString class. And I'm surprise MS didn't include these functions.
However, you can do an append by using the += operator as in my above example. And an assign can be done by either iterating through each position via SetAt, or modifying the buffer directly via GetBuffer() function.
A lot of critics of CString, don't fully understand the class, and don't realize the average peformance value.
Do a reserve() on the string before using it. Then you should see hardly any difference.Quote:
f you run the code below, you'll see the peformance difference between CString and std::string.
Run the test in release mode (not debug).
The code below will show that CString out peformance std::string by a factor of MORE then 10 to 1.
Compile in VC++ 6.0 using std::string that comes with VC++.
Almost all of the standard container classes, including std::string, has a reserve() method that addresses the issue that you demonstrated in your code. The ones that don't are the associative types (maps).
In general, what usually ends up in real code is a series of many concatenations on one string. Why not run a benchmark on that, since that is what is more likely to be done in a real application.
Regards,
Paul McKenzie
You're not being fair to std::string with your ANSI vs UNICODE example. Add this to your project:
and we're on a more equal footing, since that's what happens with CString, anyway. Then the only difference is between the two is that you have to call the c_str() function on the tstring. To my mind that is a distinct advantage that string has over CString. Take the implicit conversion out of CString and it will be a much better class. I also don't like the GetBuffer() function of CString - it opens it up to all sorts of potential problems.Code:#ifdef _UNICODE
typedef tstring std::wstring;
#else
typedef tstring std::string;
#endif
Also, I ran your code. There is hardly any difference in the times, even without reserve():
Running VC 6.0, Pentium 4, 3.00 GHZQuote:
Time duration is as follow:
LenTime1 = 109
LenTime2 = 141
LenTime3 = 93
LenTime4 = 188
LenTime5 = 203
Regards,
Paul McKenzie
That is not exactly what happens with CString. The CString type char itself is change instead of the entire object.Quote:
Originally Posted by Graham
IMHO, the advantages of implicit conversion out ways the disadvantages.
I guess we'll have to agree to disagree.
Did you run it in release or debug mode?Quote:
Originally Posted by Paul McKenzie
Those results look like debug results, and not release.
FYI:
Even in your results, the std::string is about 40% slower the CString.
That's a big difference to me.
And using reserve is not a valid argument for this test. You don't always know the size that you're going to need for your buffer in advance.
Release mode (maximize speed).
For debug, I get these numbers:
Regards,Quote:
Time duration is as follow:
LenTime1 = 234
LenTime2 = 203
LenTime3 = 344
LenTime4 = 422
LenTime5 = 437
Paul McKenzie