Solution #1 is good (but IMHO didn't answer all the questions).
I have used both CString and std::string in my travels and my feelings are that std::string is newer than CString but in my observation not as robust. CString is Microsoft-only (so not portable, but if you move your code base to another platform, you could write a complete linux CString class in about 30 minutes). Both classes give you a class that packages an array of "const char*" that can be manipulated in various ways (CString wins on features).
CString will give you a reference to const char* out of the box but with std::string, you have to ask for it with the c_str() method.
CString also allows you to reference a "char*" (ie. write access) with the GetBuffer() API. If you just use CString::GetBuffer(), then you are handed the current object as a non-const string. It is your responsibility to know how long it is. If you give a value to GetBuffer() then the buffer is resized to a minimum of that value. In other words:
CString str1 = "123"; CString str2 = "123"; char* p1 = str1.GetBuffer(); char* p2 = str2.GetBuffer(256);
CString has better internationalization characteristics that std::string.
std::string is considered more modern and unafflicted with association with MFC and ATL, so you will get a lot of people telling you that CString is "bad" without very good explanations. [Also, CString was originally bundled with MFC, but was separated into its own library somewhere around VS4.2. If anybody tells you that CString is an MFC class, they have been wrong since about 2001.]
Joseph Newcomer has written an excellent CString resource which can be found
here[
^].
I'm not sure about std::string but CString is reference counted so that you can do several interesting things with it. eg.:
CString GimmeAString()
{
CString str = "Blah Blah"; return str;
}
Note that str is created on the stack, then returned. This is normally bad juju but since the stack object points to chars on the heap and reference counted, the ref count goes to 2, the stack object is deleted (but not the heap allocation) and a valid string is returned by the method even though the stack object is gone. I consider this a questionable feature since it saves you from failure in the case of doing something dumb.
As for the strcpy() vs strcpy_s() functions, the original crt provides strcpy(), which is standard and portable. The "_s" functions are Microsoft only and provide length checking for memory movement. For example:
char s1[5];
strcpy(s1, "Hello World"); strcpy_s(s1, "Hello World");
char* p1 = new char[5];
strcpy(p1, "Hello World"); strcpy_s(p1, 5, "Hello World");