STL algorithms
UPDATE@2010-Feb-23 22:11: Thanks to Arseny Kapoulkine (his blog is : http://zeuxcg.blogspot.com/ ) I discovered that there was a bug in the code I showed. I have fixed this bug
I'm not a huge fan of the STL library (you know [cci_cpp]std::vector[/cci_cpp]and it's relatives) as I don't like the syntax and the naming of some functions and yes, I'm well aware those are mood points but then again I'm always looking for perfection. But there is one part of the STL library I really love which is STL algorithms.
For example, let's say we have the following code
#include <vector> /* ... * Somewhere in the code * ... */ std::vector<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3);
And we want to remove the number "2" from the vector, sadly [cci_cpp]std::vector[/cci_cpp] is lacking a remove function. It does have an erase function but that would require an iterator. We could switch to [cci_cpp]std::list[/cci_cpp] who does have a remove function. Without STL algorithms we would write something like this:
/* Written for readability
* And it only removes the first one it finds.
*/
template <typename T>
void RemoveFromVector(const T val, std::vector<T> vec)
{
for(std::vector<T>::iterator begin = vec.begin(); begin != vec.end(); ++begin)
{
if(*begin == val)
{
vec.erase(begin);
return;
}
}
}
/* ... And here we call it */
RemoveFromVector<int>(2, numbers);
The only thing I can say about it is that it looks ugly. Now let's use stl algorithms and see how that looks like.
/** * Previously the code was: * std::remove(numbers.begin(), numbers.end(), 2); * But that only removes the number, but keeps the same size. std::remove instead * returns an iterator which holds the end. (numbers.size() would give the same * result before and after the above code. * Thanks to Arseny Kapoulkine this has been corrected and the good version is * now here available. */ numbers.erase( std::find(numbers.begin(), numbers.end(), 2) );
One line of code and that's all. Well, it's still one line of code, but two rather simple functions. The [cci_cpp]erase[/cci_cpp] function takes an iterator as input (which [cci_cpp]std::find[/cci_cpp] returns for you). However keep in mind that if [cci_cpp]std::find[/cci_cpp] can't find a result (for example "2" was already missing, your application will crash.One thing I always hate was when I stored pointers in an object that needed destruction
#include <vector>
#define SAFE_DELETE(p) if(p) delete(p); (p)=0;
class StorageObject1;
class StorageObject2;
class StorageContainer
{
std::vector<StorageObject1*> m_Objects1;
std::vector<StorageObject2*> m_Objects2;
public:
~StorageContainer()
{
for(size_t i = 0; i < m_Objects1.size(); ++i)
{
SAFE_DELETE(m_Objects1[i]);
}
for(size_t i = 0; i < m_Objects2.size(); ++i)
{
SAFE_DELETE(m_Objects2[i]);
}
}
};
The above example is rather tame but if you have a lot of different objects it quickly becomes ugly to look at. However with a bit of ingenuity you can become a lazier programmer. Here is the same example but then easier to read
#include <algorithm>
#include <vector>
#define SAFE_DELETE(p) if(p) delete(p); (p)=0;
template <typename T> void SAFE_DELETE_FUNC(T* object) { SAFE_DELETE(object); }
class StorageObject1;
class StorageObject2;
class StorageContainer
{
std::vector<StorageObject1*> m_Objects1;
std::vector<StorageObject2*> m_Objects2;
public:
~StorageContainer()
{
std::for_each(m_Objects1.begin(), m_Objects1.end(), SAFE_DELETE_FUNC<StorageObject1>);
std::for_each(m_Objects2.begin(), m_Objects2.end(), SAFE_DELETE_FUNC<StorageObject2>);
}
}
Of course you can make the function even a bit smarter so that you don't have to write the start and end iterators, but I prefer it like this. Here you can find the other versions of STL algorithm: http://www.cplusplus.com/reference/algorithm/