MFC collection classes or STL

The new version of MFC in VS.NET has changed the implementation of the template collection classes and consequently broken my code (thanks Microsoft!).

Since I have to rework the code anyway I was wondering whether to use the STL collection classes instead of the MFC classes.

I am familiar with using both so there's no cost in switching to STL, but can someone give some pro's and con's of each in terms of performance etc.?
Who is Participating?

Improve company productivity with a Business Account.Sign Up

AxterConnect With a Mentor Commented:
If you, or anyone else wants to use STL containers, I highly, highly recommend getting Effective STL, by SCot Meyers.

The above original StringPtrLess object, came from his book.

If you read this book, you'll forget about MFC containers, and never look back.

The book gives you a taste of some of STL potensial, that most programmers never think about.
Like using the allocator to create a container in share memory.
typedef std::set<CString*, AnyObjectPtrLess<CString>, MyShareMemoryAllocator<CString*> > StringPtrShareMemSet;

Override the the compare class to create a case insensitive container.

std::set<string, MyCaseInsCompare> fooString;

These are just a couple examples of things you can easily do with STL, that you'll find difficult, if not impratical to do with MFC containers.

IMHO, the STL containers are much better then the MFC containers.

The STL containers are more portable.
You can use a custom allocator with STL containers.
The STL containers work with copy constructors.
You have more generic functions, functors, and classes that you can use with STL containers, then you do with MFC containers.
Your code will be more stable when MFC gets another hair up their ?#%#$, and diecide to change their implementation again.
If you don't like the STL that MS has on their compiler, you can replace it with another STL, and there are at least three FREE STL libraries that you can download from the web.
Some of the FREE STL libraries come with extra goodies, like hash_map, hash_set, rope, etc...
These goodies are not available in MFC, and there are no counter parts to them.
The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

OK, I'll try in pieces:

I've see a lot of EE Questions about why the STL container stopped working when it was in a DLL.  As I understand it, that is a serious, inherent flaw with STL containers.  

The other problem with STL apparently grates on my nervers more than some other people.  I don't know about you, but I spend a lot of time in the code editor fixing syntax errors and in the debugger fixing program bugs.  Call me silly, but:

     vector<string> asNames;
     sLastName= asNames[2];
never mind the push_back is a really stupid name ofr a function that adds an item to a vector... That sequence  causes an error, of course.  And here is what the debugger brings up:

     _Myt& assign(const _Myt& _X, size_type _P, size_type _M)
          {if (_X.size() < _P)
          size_type _N = _X.size() - _P;
          if (_M < _N)
               _N = _M;
          if (this == &_X)
               erase((size_type)(_P + _N)), erase(0, _P);
          else if (0 < _N && _N == _X.size()
               && _Refcnt(_X.c_str()) < _FROZEN - 1
               && allocator == _X.allocator)
               _Ptr = (_E *)_X.c_str();
               _Len = _X.size();
               _Res = _X.capacity();
               ++_Refcnt(_Ptr); }
          else if (_Grow(_N, true))
               {_Tr::copy(_Ptr, &_X.c_str()[_P], _N);
               _Eos(_N); }
          return (*this); }

The same error with a CStringArray brings up:

_AFXCOLL_INLINE CString& CStringArray::ElementAt(int nIndex)
     { ASSERT(nIndex >= 0 && nIndex < m_nSize);
          return m_pData[nIndex]; }

I'm sure that Axter can read the former, but I'm equally certain that I can't and I don't want to spend even 10 or 20 milliseconds wasting my time trying to.  But wait! There's more!

This is is really good.  Here is a not-too-hard to make syntax error:

     typedef list<string, allocator<string> > LISTSTR;
     LISTSTR test;

     test.insert(1, "test");

And (oh, this is RICH...) here is the error message:

D:\MyProj\zEETests\Console6\Console6.cpp(50) : error C2664: 'class std::list<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,
struct std::char_traits<char>,class std::allocator<char> > > >::iterator __thiscall std::list<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,
class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::insert(class std::list<class std::basic_string<char,struct std::char_traits<char>,
class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::iterator,const class std::basic_string<char,
struct std::char_traits<char>,class std::allocator<char> > &)' :
cannot convert parameter 1 from 'const int' to 'class std::list<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,
class std::allocator<char> > > >::iterator'
        No constructor could take the source type, or constructor overload resolution was ambiguous
Error executing cl.exe.

(No kidding, this is the first syntax error that I tried when researching this post.)  

But don't worry, you can always go to the help file, which says:
Recheck the prototype for the given function and correct the argument noted in the error message. If necessary, an explicit conversion may need to be supplied.

So you zip out to the wealth of documentation on the list.insert function:

iterator insert(iterator it, const T& x = T());
void insert(iterator it, size_type n, const T& x);
void insert(iterator it, const_iterator first, const_iterator last);

That kind of horsepucky will cost you and your team hundreds of hours of grief.   Life is just too short.

-- Dan
I like MFC.
Microsoft wants us to use it and that's all that matters!

<sorta kidding>

I have studied both and prefer (for code I KNOW is non-portable) ....... MFC.

You (we) should know both (if available).
If you're using a Microsoft compiler and writing Windows (or DOS) code, why not take advantage of it.  If you're writing a GUI (on Windows), you will NOT escape MFC (and keep your sanity).

You will still need to know how to use STL if you're going to survive in the market place.
I don't think the questioner is asking if we should use MFC or STL.
I think the question is refferring specfically to the MFC containers VS the STL containers.




Can you please clarify?
>>iterator insert(iterator it, const T& x = T());
>>void insert(iterator it, size_type n, const T& x);
>>void insert(iterator it, const_iterator first, const_iterator last);
>>That kind of horsepucky will cost you and your team
>>hundreds of hours of grief.   Life is just too short.

IMHO, that prototype is very easy to understand if you know STL.  If you don't know STL, then of course you're not going to understand it.

Correction for your code would be the following:
test.insert(test.begin(), "test");

Or even better:

The questioner stated that he/she is familiar with both types, therefor, I don't see your example being an issue, because anyone who understands STL containers would easily be able to figure out the prototypes you posted.
>>never mind the push_back is a really stupid name ofr a
>>function that adds an item to a vector...
One of the nice things about STL, is that it has a more conformed naming convention.
If I want to add an item to a vector, deque, or a list I can use the following.

AnyNonAssociatedContainerType<int> Foo;

push_back is much more informative then Add.  Since some containers can use push_front, push_back tells you exactly where it's going.
Add, doesn't tell you anything about the specific destination point.

With the MFC containers you have the following:
CList::AddTail  //Sounds a lot like push_back to me????
So to use these containers I have to memorize two sets of naming convention.

CStringList sl;
CStringArray sa;

This is just one example of how the MFC containers lack the consistency that the STL containers have.
Dan>>>> this type of horsepucky...
Axter>>...would easily be able to figure out the prototypes you posted.

I suppose that you could read that post -- through rose-colored glasses -- and believe deep in your heart that the 'horsepucky' was in reference to the prototypes I listed at the end.  In fact, the prototypes are not too hard to understand and I have learned something about presentations -- never end with your weakest argument.

Axter, did you *miss* THAT TWELVE HUNDRED CHARACTER ERROR MESSAGE?  You must have because you didn't even address it in your post.  Even the most dyed-in-the-wool STL advocate would need to agree that *that* is FIRST CLASS HORSEPUCKY.

Anyone up for a challenge?  
Who here can produce an example of a 5000-byte compiler error message caused by a one-character syntax typo?  That is no challenge with STL of course, so to make it interesting, bonus points will be awarded if you can also make the error message contain the entire Gettysburg Address.

-- Dan
I think using STL is always better than MFC. In my knowledge STL is faster and you have more control over you code during Reengineering.

I agree with you, in that the error message is very weak.
BUT, I don't see this as an STL problem.  It's more of a compiler problem.

If MS put more work into their compiler, they could easily fix it so you would get a better compiler error.

MS VC++ weak error messages are not enough to get be to use the MFC weak containers.
I rather have the strong robust STL containers and deal with the error messages, then to have to deal with MFC containers.
paulburns: thank you for spaning a flame war!
DanRollins: thank you for your heartily comments!

OK to throw in something of value:

1) It's mainly a question of style. To many "good point"s on both sides
2) MFC containers limit the portability of your code even in the microsoft world - even in the microsoft world, you're limited to MFC. If you need to take a snippet to an ATL or Win32 applicaiton, you're lost.
3) Writing truly portable STL code isn't straightforward either; at least portability doesn't "come for free".
4) Programmers that don't know either tend to get along better with MFC containers. (This is the next discussion: Should the skills of the team at hand influence the chosen technologies? Typically STL-afficionados say "no never")
4a) in other words, the learning curve of STL containers is much much steeper. You need to learn a lot before you can really *use* STL (beyond the "vector<foo> instead of foo *" level).
4b) STL is often counter-intuitive. I guess Axter and Nietod will strongly object, but that's what all persons I worked with and me found until now.
5) MFC containers often mix better with the Win32 API and "real life" requirements. MFC Containers work well with CString, CString works perfectly with Win32 and MFC.
6) STL has the wider range of containers - so if you need something MFC doesn't have, it might be better to choose STL and avoid mixing both libraries.
7) STL is the standard, MFC is not. This is IMO the strongest argument for STL, since the time you invest here will some time pay back. However, we often find ourself in a situation where we're paid not for time invested but for a job being done.


When choosing beetween Scylla and Charybdis, there's no way in the middle.
Incidently, paulburns, what broke?  I'd like a 'heads up' because I just started exploring the new release.  Thanks!

What is the output of some other compiler for the same error?  I suppose a better error message would be along the lines of:

:\MyProj\zEETests\Console6\Console6.cpp(50) : error C2664: Don't EVER do that again!

And of course it is not just the error message.  Debugging is severely hampered by the template-within-a-template-within-a-template definitions.  You can't just click on an object in the debugger and get to its meat.  Give me a good clean (non-templatized) class with well-documented methods and attributes any day.

-- Dan
>>in other words, the learning curve of STL containers is
>>much much steeper. You need to learn a lot
>>before you can really *use* STL (beyond the "vector<foo>
>>instead of foo *" level).

I agree, and (IMHO) this is the ONLY strong argument for not useing STL containers.  
STL is harder to learn, and in part because it has so much much more to offer then the MFC containers.
But since the questioner stated the he/she knows both, it shouldn't really matter.

>>MFC containers often mix better with the Win32 API
>>and "real life" requirements.

I disagree.  I haven't found any situation in which the MFC containers works better for Win32 API's.
Do you have an example?

>>MFC Containers work well with CString.
The same can be said for STL containers.  Just because STL has std::string, it doesn't mean you can't use CString with it.
     std::vector<CString> MyCString;
     CString SomeStr;
     for(int i = 0;i<3;++i)
          SomeStr += MyCString[i] + " ";
Above code works just fine.

For the most part, I agree with everything else.
paulburnsAuthor Commented:
I didn't realise everyone was so passionate about collection classes!

I did find STL a steep learning curve and it does use some strange terms e.g. 'vector' for array, and some strange ways of doing things e.g. to append one array onto another you use the 'insert' method.

Will probably stick with MFC for this project but use STL in future.

What broke?....

Im MFC 7.0 they removed the ConstructElements, DestructElements, etc. helpers. My app had a collection of pointers to objects and I was using DestructElements to delete these objects when elements of the collection were removed. So now, in order to have pointers to objects in a collection, I will need some kind of reference counting smart pointer... same as if using STL. No major effort, but a pain.

But I have now discovered another problem... I was using CompareElements to compare the actual objects in the collection, rather than the pointers, for searching operations. Now CompareElements is gone and an object's == operator is used to compare elements when searching, so all it's going to do is compare the pointers and not the actual objects.

Can anyone help on a way around this? I believe the same problem would exist with the STL classes?
Can't you override operator== ?
-- Dan
>>Can anyone help on a way around this? I believe the same
>>problem would exist with the STL classes?

The STL classes do not have this problem.
You can easily override the compare class used for the container.

struct StringPtrLess:
std::binary_function<const std::string*, const std::string*, bool>
     bool operator()(const std::string* ps1,
          const std::string* ps2) const
     { return *ps1 < *ps2; }

typedef std::set<std::string*, StringPtrLess> StringPtrSet;

//Example using above type:
void SomeFunctionX(void)
     const int MaxQty = 8;
     std::string MyUnsortedStringList[MaxQty] = {"David","Bob","Joe","Bill","Gates","John","Marry","Axter"};
     StringPtrSet spp;
     for (int i = 0;i < MaxQty;++i)
     std::string Collection;
     for (StringPtrSet::iterator s = spp.begin();s != spp.end();++s)
          Collection += *(*s) + ", ";
The StringPtrLess structure can easily be modified to be more generic.
template<typename T>
struct AnyObjectPtrLess:
std::binary_function<const T*, const T*, bool>
     bool operator()(const T* ps1,
          const T* ps2) const
     { return *ps1 < *ps2; }

typedef std::set<CString*, AnyObjectPtrLess<CString> > StringPtrSet;

//Example using above type:
void SomeFunctionX(void)
     const int MaxQty = 8;
     CString MyUnsortedStringList[MaxQty] = {"David","Bob","Joe","Bill","Gates","John","Marry","Axter"};
     StringPtrSet spp;
     for (int i = 0;i < MaxQty;++i)
     CString Collection;
     for (StringPtrSet::iterator s = spp.begin();s != spp.end();++s)
          Collection += *(*s) + ", ";

See how easy CString works with MFC.  Try doing that with a MFC container.

Even if you like using CString, you can still do more with an STL container holding CString, then you could with an MFC container.

And by the way, I do prefer CString over std::string.
MFC has some really nice classes.  I just don't think that their container classes fits in to that catagory.
See how easy CString works with STL containers!
paulburnsAuthor Commented:
Hmm, very interesting. I think I have a lot more to learn about STL.

Thanks to everyone for thier thoughts.
Axter:  I'm (almost) through Effective STL, and it has been an eye-opener in both ways. But finally it only solidified (tr?) my opinion about STL: It's a great and powerful library, but IMO it has some serious drawbacks that make it a suboptimal *standard* library.

>> mix with real life requirements
The "canonical" being string not guaranteed to be contiguous memory, and misisng (I guess) conversion ANSI <--> wide string. my incapability to write a CoTaskMemAlloc allocator for arrays and SysAllocString allocator for strings. (and my fear If I do it will it be correct?)
I know that vector<char> can be a useful replacement, and you can do everything... but hell, my boss doesn't care (and that's ok!)

I currently live in a big mess: 3 CString classes (one the MFC native implementation, one MFC ripoff to be used in ATL components, and new on stage the CString class from WTL), additionally the _bstr_t class for COM strings and ANSI <--> UNICODE conversions, 5 array class templates (some old "homegrown" from a no-dependency-lightweight library, wrappers for COM Counted Arrays (one auto-deleting, one not), and of course STL vector<> and MFC CArray.

I would all the deep and dark corners of STL for using one single array class in this project, but...

replacing the homegrown with something else would be a major rewrite that benefits no one, and STL would have some (minor) performance tradeoffs that require major investment into STL coding, the Counted Arrays must be manually memory-managed for performance reasons, and with the MFC containers I'm usually more productive. (And this matters if my boss asks "how long will it take - one day, two?")

That's how real life is, and STL can't save me.
>>ANSI <--> wide string.
Why can't you use the wstring?

>>The "canonical" being string not guaranteed to be
>> contiguous memory
Where did you read this at?
std::string doesn't have a guaranteed to have it's own unique memory slot, just like CString.  They both do refference counting.
But as far as I know, std::string does have a guaranteed to be continuous memory.
Are you reffering to the object itself, or what c_str() points to?

>>my incapability to write a CoTaskMemAlloc allocator for
>>arrays and SysAllocString allocator for strings
I'm in the middle of creating one myself.

Do you have something like this for the MFC containers?
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.