Written by Christian Stigen Larsen
This is a basic C++ string class I made for fun. It's not fancy — but it's simple and should be easy to understand.
You can browse the source code in my_string/ or download my_string.tar.gz.
I'll explain some of the tidbits.
What I find most interesting here is the global friend operator:
friend string operator+(const string& lhs, const string& rhs);
If it looks confusing, remind yourself that it's just a function. It is called operator+, takes the two arguments lhs (left-hand side) and rhs (right-hand side). So when the compiler parses
my::string s1, s2; s1 = "foo" + s2;
I visualize that it does something like this:
string operator+(const string& lhs, const string& rhs)
{
return string(lhs) += rhs;
}
Self assignment is when you do something like
s = s;
or, perhaps more realistically, something like
my::string s = "hello world"; // ... const char *p = s.c_str(); s = p + 6;
The point is that if you're unaware of potential self-assignment problems, you could end up doing something like first freeing a specific memory region and then proceed to copy from that freed region.
The usual way of avoiding self-assignment is to write
A& operator=(const A& a)
{
if ( this != &a ) {
// ...
}
return *this;
}
In my::string I do
string& string::operator=(const string& s)
{
return operator=(s.p);
}
string& string::operator=(const char* s)
{
if ( p != s ) {
// this should work with overlapping memory
char *copy = strdup_never_null(s);
free(p);
p = copy;
}
return *this;
}
Notice that I copy the memory region before freeing p. If had freed p before the copy, the above code would be broken in situations like my::string s = "hello"; s = s.c_str() + 2;.
If we indeed write
s = s;
then nothing will be done. However, if we do
s = (s.c_str() + 1);
then we will call strdup_never_null, free and then assign. Because we use the temporary variable copy we won't run into problems.
In almost all of the STL you'll find the empty() function. As Scott Meyers notes in Effective C++, empty() is either faster than a call to size() or just fast. But in most cases much faster.
In my::string, a call to size() will count the number of characters in the entire string, but empty() just checks to see if the first character is '\0' — obviously much faster.
To see a complete version, you should check out some std::string implementations.