25

Using C++ with MFC. Coming from a C# background I typically just use string for all, well, strings. I use them for class members, method parameters, and method return values.

Now in C++ I've got std::string, CString, char *, LPCTSTR, and more. As I design my data members, method parameters, and method return values which type(s) should I be using? Ease of use is important and CString seems to offer that but my instinct is toward portable standards although portability is pretty low on my list of priorities (now). Also, I don't like the c semantics of creating string buffers and passing them into methods and functions.

I think from an immediate ease of coding perspective CStrings probably have the edge. But, overall, what is the "high code quality" way to do this?

EDIT:

I'm especially concerned about the interface points in my code (i.e. method parameters and return values). E.g.:

Shape::SetCaption(const char *caption) {...}

Shape::SetCaption(CString caption) {...}

Shape::SetCaption(std::string caption) {...}

Shape::SetCaption(std::wstring caption) {...}
User
  • 62,498
  • 72
  • 186
  • 247
  • @Christian: Writing a plugin for a platform that is implemented in MFC and must integrate tightly with it. I tried to go down the Qt path but Qt/MFC integration was an uphill battle. If it were an option I would choose C#. – User May 24 '11 at 23:24
  • Agree with Christian Rau's comment. MFC is not a developer-friendly framework. QT is way to do GUI more simple. – Moses May 27 '16 at 07:07

4 Answers4

23

I usually prefer to adapt my coding style to the framework I'm working in to be consistent with it. So when I work with MFC (which i haven't for a long time), I prefer the use of CString (and LPCTSTR as function arguments in public interface methods). When working with Qt I prefer QString and Qt's containers over STL containers and for everything not directly related to such a framework I use std::string as it's the standard C++ way of handling strings.

It doesn't make such a huge difference, since they all offer more or less equal functionality (and are easily convertible into each other) and when code is written for a certain framework, it depends on it anyway, so portability is not such a huge concern.

Just don't bother with plain char arrays! And by the way, try to pass objects by const reference (const std::string &caption) and not by value, as in C++ variables are not automatically references and copying a string can get quite expensive.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • I sometimes use plain char arrays when I need a temporary buffer for something, although I've had good luck with `vector` too. – Mark Ransom May 24 '11 at 22:46
  • Makes sense, but would you use CString even in a static library you build to be used by your MFC project? – User Jun 03 '11 at 20:33
  • @User If the library itself doesn't use or depend on MFC and could possibly be used with another project, then definitely not. But if it is bound to your project anyway, I'm not quite sure. I would use `std::string` in everything not directly realted to MFC for better code reuse and flexibility. – Christian Rau Jun 04 '11 at 11:50
9

MFC was written with the expectation that you'd use CString. This is especially apparent when a function uses a parameter to return a string. For example, compare these two calls to GetWindowText:

CString s1;
wnd.GetWindowText(s1);

std::wstring s2(SOME_MAX, 0);
int len = wnd.GetWindowText(&s2[0], s2.size());
s2.resize(len);

Converting between the two isn't bad though, so you might compromise by using std::wstring for most things and a temporary CString when necessary.

CString s3 = s2.c_str();
std::wstring s4 = s1;

Edit: There may be a way to automate the temporary CString. Fair warning, this is a complete hack. I haven't tried this, so no guarantees - you'll probably get warnings about binding a temporary to a non-const reference, but you can turn those off.

class PopString : public CString
{
public:
    PopString(std::wstring & final) : m_final(final)
    {
    }

    ~PopString()
    {
        m_final = (PCTSTR) *this;
    }
private:
    PopString(const PopString &) {}  // private copy constructor to prevent copying
    PopString & operator=(const PopString &) {}  // private copy operator

    std::wstring & m_final;
};

std::wstring s5;
wnd.GetWindowText(PopString(s5));
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
3

If you care for portability and you're using C++ use std::string. No point going low-level with char arrays if you don't need to. If you don't care for portability and the platform-provided strings provide more of the features you need, by all means, use them. They might actually be more optimized for the platform.

rid
  • 61,078
  • 31
  • 152
  • 193
3

Do not use CString. It uses a COW implementation which is very vulnerable to things like threading. Do not use char* or LPCTSTR (which is just const char* or const wchar_t* under a different name), as they do not manage their own memory. Use a std::string for 8-bit codepoints or std::wstring for 16-bit codepoints on Windows (32bit for Unix).

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • It's `LPCTSTR`, and `char const*` or `wchar_t const*` respectively. – ildjarn May 24 '11 at 23:03
  • 6
    Its not like std string is threadsafe. But -1 because when using mfc CString is usually the way to go. – John Dibling May 24 '11 at 23:12
  • 5
    @John Dibling: `std::string` is thread-safe in a way that CString isn't. If you have two independent `std::string`, you can guarantee that they're safe to use on two threads. The same cannot be said of CString. – Puppy May 25 '11 at 09:01
  • @DeadMG I thought, that's what you cannot guarantee, as STL implementers are free to use implicit sharing for strings. But I've heard the new C++0x standard removes this freedom and therefore guarantees really threadsafe `std::string`s. – Christian Rau May 25 '11 at 16:45
  • 2
    @ChristianRau : The problem is only fixed *officially, in the standard,* as of C++11, but the problem was formally acknowledged in Defect Report 530 over five years ago and has been implemented in every major standard library implementation since then (if not earlier). – ildjarn May 26 '11 at 07:14
  • 1
    @ChristianRau: Yeah- the Standard has only just banned it, but nobody did it before then anyway. Just like how technically, `std::string` doesn't have to have continuous memory, but in fact pretty much every implementation does. – Puppy May 26 '11 at 09:33
  • 2
    @DeadMG: Is there a link to an article or SO question that 2 instances of a CString cannot be used independently in different threads? Sounds pretty serious if this is really the case. Is it some sort of special case, or is it generally the case? I will be using VS 6.0. Is there a newer version of VS that fixes the problem? – Bob Bryan Jun 17 '14 at 14:35
  • 1
    They can be used independently in different threads, but they're insanely slow whilst doing so. But, if you're using VS 6.0 then you have bigger problems. Like the fact that your compiler can't generate correct code to save it's life. Or accept anything like correct C++. – Puppy Jun 17 '14 at 21:07
  • 1
    I agree with this answer, except don't ever bother with std::wstring. Always use std::string. If you want unicode, std::string can be treated as UTF-8 (see http://utf8everywhere.org/). – JDiMatteo Mar 04 '15 at 20:52
  • 2
    The threading rules are [explicitly spelled out](https://msdn.microsoft.com/en-us/library/h14y172e.aspx). If that is not good enough for you, implement custom synchronization, or use a different string class. With MFC code it's almost always best to go with `CString`'s. All the more, if you are using COM as well. I will also assume that all occurrences of *"codepoints"* should read *"code units"*. Maybe you can at least fix that. – IInspectable Mar 02 '17 at 20:23
  • 1
    CString just has another implementation than std::stiring. It saves memory and improves single-threaded performance at the expense of worse multithreaded performance. Which is a good tradeoff for a single-threaded GUI. And it doesn't have this std::string(nullptr) -> exception bullshit, for example. So CString vs std::string choice depends on your requirements and limitations. – Steed May 08 '18 at 08:17