Questions regarding moderate level C++

Started by
22 comments, last by snk_kid 19 years, 1 month ago
I've once again started towards trying to shake off some of my C habits and learn some of the more C++ specific conventions. As usual it's only brought more questions and headaches. So, on to the questions, in no particular order: 1 - I first started looking at C++ strings. I imagined they'd be the simpilest, and provide the most direct utility, allowing me to gain motivation for learning more. Well, I was right on the first part. They certainly are simple it seems. So, from what I can tell, they only take simple assignment, concatination, insert/remove, and find. Is that it? Is there no fancy automatic conversion like cout allows with multiple types? What exactly does this gain over char*'s beyond automatic bounds control and memory management [admittedly non-trivial]? And I'm curious as to the bounds control since char*'s will still be needed as an intermediary for the conversions... I also found some references to stringstream or strstream, which might be the same class, or not... That seems to allow the magical iostream formatting. Either way, only strstream seems to exist on my machine, and even then passing it to cout doesn't do the expected. I hope I'm simply missing something, and C++ strings -do- kick ass and take names, but right now it's not looking like it. 2 - Part of my problem with the STL I wager is my lack of experience with templates. Part of my problems learning templates is the trival examples that are always given with them. What sort of slightly more than trivial problems would be good practice problems for a template solution? The two that I came up with, a hash table with templated keys and a serialization setup, both ran across blocking problems which would've modified the design to be less featureful than their current non-templated incarnations. [aside: The hash table ran across a problem that the function pointer contained within to hash the key could not be used, as something like this:

template <typename K>
hash<K>::hash(int inbuckets=DEFAULT_BUCKETS, int (*in_algo)(K *,int
    =&default_hashing_algorithm(K *,int));
Which is perhaps beyond even the 'slightly more than trivial' I was shooting for, but... The compiler came back with "sorry, not yet implimented." and something about dump_* on this line. I didn't imagine something like that was feasible anyways, and proceeded to other things. Serialization ran into the problem that functions cannot be overloaded by return type only.] Both of them probably could've been worked out with more persistance and perhaps designing around the templates rather than converting them. Still, I'm not too convinced that templating would've really helped too much in either case. Still, I think templating is something I'm far too inexperienced with, and history has shown I need something very practical to really *get* something [linked lists for pointers, scene graph for inheritance]. I'm sure I'll have more once I actually get to the main part of the STL, if I don't get frustrated and avoid it again.
Advertisement
Just a quick reply, as I'm passing through.

The book that opened my eyes to templates was Modern C++ Design; a link can be found here; this link contains a couple of sample chapters from the book, which may give you some template ideas.

As for string - I admit to only using it's basic functionality - but as I understand it, the functionality is greater than you're describing - essentially they're a container class of chars, so many of the STL algorithms are compatible with them. For example, I use string names to load resources from a factory class - the factory stores a map of file extensions and associated loading type - so it first finds the last bit of the string (i.e. .bmp) and then chooses the appropriate loader (class LoadBMP). This is routine to do with strings, like so:

	std::string::size_type index = filename.rfind('.');	std::string extension = filename.substr(index + 1);	theResourcesTypedef_::iterator it = theResources_.find(extension);


HTH,
Jim.
It sounds to me like you need this book: "C++ for Game Programmers" by Noel Lloepis. It's excellent, and it's sole purpose is to cover how to effectively use the exclusive features of C++. Templates actually has an entire chapter devoted to it.

In the book, he writes a linked list class using templates as his main example.

Here's his class declarations so you can see templates being applied to something slightly more advanced and useful:

template<class T>class ListNode {public:     ListNode(T);     T & GetData();     ListNode * GetNext();private:     T m_data;};template<class T>class List {public:     ListNode<T> * GetHead();     void PushBack(T);     // ...private:     ListNode<T> * m_pHead;};// here's some examples of using the List class:List<int> listOfIntegers;List<string> listOfStrings;List<GameEntity*> listOfGameEntities;


I'm sure you can see how incredibly useful templates are in this case. Without the List and ListNode classes being templatized, you would have to rewrite both the classes for every single type of data you might want to use them to store. Plus, doing this would rule out the possibilty of using polymorphism (which is VERY useful for storing a list of game entities).

Well I hope this helps,
Zach.
Quote:Original post by Telastyn
1 - I first started looking at C++ strings. I imagined they'd be the simpilest, and provide the most direct utility, allowing me to gain motivation for learning more. Well, I was right on the first part. They certainly are simple it seems.

So, from what I can tell, they only take simple assignment, concatination, insert/remove, and find. Is that it? Is there no fancy automatic conversion like cout allows with multiple types? What exactly does this gain over char*'s beyond automatic bounds control and memory management [admittedly non-trivial]? And I'm curious as to the bounds control since char*'s will still be needed as an intermediary for the conversions...

I also found some references to stringstream or strstream, which might be the same class, or not... That seems to allow the magical iostream formatting. Either way, only strstream seems to exist on my machine, and even then passing it to cout doesn't do the expected.

I hope I'm simply missing something, and C++ strings -do- kick ass and take names, but right now it's not looking like it.


strstream is deprecated, this was a pre-standard class, std::(i/o)stringstream are the ones you want, they are actually type aliases (typedefs) for templated classes just as std::string is, they are independent of specific character type (and some cases allocation scheme too) there-for can be used for localization. An e.g of using a stringstream is:

#include <string>#include <sstream>#include <iostream>int main() {   std::string s("1.3 10000");   std::istringstream iss(s);   float f; int i;   iss >> f >> i;   std::cout << "float: " << f << " int: " << i << std::endl;   std::ostringstream oss;   oss << "hi " << f << ", " << i;   std::cout << oss.str() << std::endl; //"str" returns a C++ string}


a more exotic example:

#include <algorithm>#include <iterator>#include <vector>#include <string>#include <sstream>#include <iostream>int main() {   const std::vector<int>::size_type N = 20;   std::vector<int> v;   v.reserve(N);   std::generate_n(std::back_inserter(v), N, std::rand);   std::stringstream ss;   std::copy(v.begin(), v.end(), std::ostream_iterator<int>(ss, ", "));   std::string s((std::istreambuf_iterator<char>(ss)), std::istreambuf_iterator<char>());   std::cout << s << std::endl;}


Not very useful example just shows you whats possiable, if you need something more advance check up boost's string algorithms library.

By the sounds of things you have an old compiler that isn't very standard compliant time to upgrade [smile].

Quote:Original post by Telastyn
2 - Part of my problem with the STL I wager is my lack of experience with templates. Part of my problems learning templates is the trival examples that are always given with them.

What sort of slightly more than trivial problems would be good practice problems for a template solution?


Lots but if your knowledge of templates is no more than a the "container of T" its time to investigate, as already mentioned by JimPrice that is one of the best books to start looking, some keywords to look into:

static polymorphism (compile-time polymorphism), type traits, meta-template programming, expression templates, compile-time dispatch, Policy-Based Class Design, automatic type erasor, functors, Domain-Specific Embedded Languages (DSEL a special little language with-in C++ built out of C++ [smile]), (off the top of my head). Look into the boost library and loki library.

You'll probably need to know about templates & functors fully before moving on to the above techniques and compiler that supports it properly especially partial template specializations that is very important.

Quote:Original post by Telastyn
Still, I think templating is something I'm far too inexperienced with, and history has shown I need something very practical to really *get* something [linked lists for pointers, scene graph for inheritance].


Yeah by the sounds of things you might actually be better off starting with something like The C++ Programming Language Special Edition (i'm not trying to be patronizing, its really good for learning C++ quite deeply) then moving on to more advance books like the one JimPrice suggested.
Thanks for the replies so far!

I've not delved into STL algorithms much yet, but don't the algorithms already apply to arrays? And shouldn't they already work with char arrays? Something to at least keep in mind though :]

A templated linked list is the standard trivial example. I was looking for something a little more. I've a non-templated linked list class I'm currently using, and templated lists show no clear cut advantage over it. Further, the STL will provide any templated data structure I'd want...

And more questions!

With STL container classes, storing pointers is 'safe', within normal pointer practices, correct? And I am responsible for free()/deleting the pointer, as the container will not do that, correct?

With iterators, is the standard iteration:
for (iterator=container.begin(); iterator!=container.end(); ++iterator){     ...}

?

Does container.end() get called each iteration, or is that something which will be optimized out nicely. Is there a .bad() check for the iterator that should be used instead?


More later.

[edit: snk_kid: Ah yes, probably about 6 years old now... Anyway, thanks for the example, I had merely forgotten the () for sstream.str.

Further, the first type traits link I found was *very* helpful in providing a good practical glimse at what templates can do that is otherwise impractical!]

[Edited by - Telastyn on March 7, 2005 6:50:19 PM]
Quote:Original post by Telastyn
I've not delved into STL algorithms much yet, but don't the algorithms already apply to arrays? And shouldn't they already work with char arrays? Something to at least keep in mind though :]


Sure, there generic any raw C-style array will do.

Quote:Original post by Telastyn
With STL container classes, storing pointers is 'safe', within normal pointer practices, correct? And I am responsible for free()/deleting the pointer, as the container will not do that, correct?


Yep, generally its actually more efficent to store by value than by reference (it really depends on the context how-ever in some cases its not possible or feasiable to, for e.g polymorphic types can only behave polymorphically via storing pointers) and all STL containers are parameterized my allocator type (last template type parameter) so you can even use a pooling scheme.

Quote:Original post by Telastyn
With iterators, is the standard iteration:
for (iterator=container.begin(); iterator!=container.end(); ++iterator){     ...}

?

Does container.end() get called each iteration, or is that something which will be optimized out nicely. Is there a .bad() check for the iterator that should be used instead?


It's generally preferred to use the standard library algorithms over hand-written loops for a number of reasons that are beyond the scope of this thread [smile].

Yes in most cases end will be repeatedly called in that code and post-incrementing iterators (i know your not just illustrating a point) actually costs performance this is just one out of many reasons to perfer the STL algorithms, if your doing any operation that does not invalidate iterators and not modify the container you can make a cache of end e.g.

for(iterator itr = container.begin(), end = container.end();    itr != end; ++itr) ;


By the way you may want to re-read my previous post because i corrected mistakes and added some more things [smile]
Quote:
I've not delved into STL algorithms much yet, but don't the algorithms already apply to arrays?


You know, I don't know. Now I need to do some reading. And I was having such a pleasant afternoon as well[smile].

Quote:
With STL container classes, storing pointers is 'safe', within normal pointer practices, correct?


Yup, with some caveats (that caught me out the first time I wrote an A* path-finder).

Some containers move themselves wholesale under some conditions (the standard example is std::vector, which normally uses an array as it's underlying implementation - and when you try and resize it beyond it's current capacity, the whole thing is moved, as opposed to any appending. This means that pointers to elements in the std::vector no longer point to what you think they're pointing at...).


Quote:
And I am responsible for free()/deleting the pointer, as the container will not do that, correct?


Entirely correct. Destruction of the container is guaranteed to run the destructor of each element, but if it's a pointer it just means you lose the pointer to the allocated memory. Leak-tacular. Of course, you could have containers of smart-pointers, which would go someway to alleviating this problem.

Quote:
With iterators, is the standard iteration:


for (iterator=container.begin(); iterator!=container.end(); ++iterator){
...
}


?

Does container.end() get called each iteration, or is that something which will be optimized out nicely. Is there a .bad() check for the iterator that should be used instead?


Yup, although the iterator needs classifying (that is, a std::vector<int>::iterator is different to a std::vector<char>::iterator - it won't automatically pick up the container type).

The alternative solution is to use an algorithm - the normal example is for_each, as follows:

std::for_each(container.begin(), container.end(), something);

where 'something' may be a function, functor or member-function. There's some funky syntax that comes up with this though, under certain condition (like bind2nd and that sort of thing).

According to my reading of Meyers Effective STL, container.end() may well get called every iteration - don't know if it will be optimised away. This is one reason for advocating use of algorithms, although there are others. For example, algorithms are usually partially specialised on container types to provide more efficient iteration through through the container than you get with your loop as written.

Not sure what you mean by a .bad() check? container.end() points to one element past the last element in your container, a handy little feature that means that the loop automatically won't evaluate if the container is empty. Or are you talking about modifying the elements of the container during iteration?

As always, a useful reference for the STL is the SGI website.

HTH,
Jim.

Edit : oops, whatever snk_kid said (I know my place.....)
Quote:
With STL container classes, storing pointers is 'safe', within normal pointer practices, correct? And I am responsible for free()/deleting the pointer, as the container will not do that, correct?


Pointers are fine. You must release the pointer manually (clear() or erase() won't do it for you) as you have assumed.

Quote:
(snipped code)

Does container.end() get called each iteration, or is that something which will be optimized out nicely. Is there a .bad() check for the iterator that should be used instead?


That depends largely on the compiler. I imagine if the compiler can determine that a function invoked from the guard condition has a cacheable result, it will cache it instead of calling the function every time. Which basically means the answer to your question is "maybe," depending on the compiler and the container type (some may have more trivial implementations of end(), et cetera).

[EDIT: Actually, I imagine the C++ standard dictates whether or not functions in the guard need to be called every time, or can be cached. I don't know where to find a copy online though -- anybody know?]

There is no bad() method for iterators -- that'd be a chore to implement, and might be semantically a bit awkward, as end() returns an interator that is actually one element beyond the last in the container.

[EDIT: I type slow. :( ]
Quote:Original post by JimPrice
Quote:
With iterators, is the standard iteration:


for (iterator=container.begin(); iterator!=container.end(); ++iterator){
...
}


?

Does container.end() get called each iteration, or is that something which will be optimized out nicely. Is there a .bad() check for the iterator that should be used instead?



Not sure what you mean by a .bad() check? container.end() points to one element past the last element in your container, a handy little feature that means that the loop automatically won't evaluate if the container is empty. Or are you talking about modifying the elements of the container during iteration?


For iostreams, the iostream should be checked with .bad() or .is_good(), which encapsulates IO error, or eof or anything else. I was just wondering if there was a similar syntax, even the iterator being null... the current container class I use has a syntax like this:

container *c;// stuff...for (container *ptr=c; ptr ; ptr=ptr->next){    //more stuff...}


so my thinking is rather stuck on an invalidated [for whatever reason] iterator being not any value, rather than equating to a special value (despite those statements being fairly equivalent).
Thanks - still don't know the answer - sigh, more reading...

But, on the topic of bad iterators. I give you:

Guru of the week 18 - iterators.

Jim.

This topic is closed to new replies.

Advertisement