Contents

  1. A simple C++ string class
  2. Download
  3. Global friend operator+(string, string)
  4. Avoiding self-assignment
  5. Always use empty() instead of size()==0
  6. A full std::string implementation

License

Creative Commons License
This work is dedicated to the Public Domain.
Valid XHTML 1.0 Strict

A simple C++ string class

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.

Download

You can browse the source code in my_string/ or download my_string.tar.gz.

I'll explain some of the tidbits.

Global friend operator+(string, string)

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:

  1. Construct s1 and s2
  2. It finds friend string operator+(string, string) as a candidate for "foo" + s2
  3. It sees that "foo" can be implicitly constructed as a string object via my::string(const char*)
  4. The operator+ function is called, "foo" is now a temporary my::string object, named lhs:
    string operator+(const string& lhs, const string& rhs)
    {
    	return string(lhs) += rhs;
    }
    
  5. This function uses the return value optimization so that compilers can remove one temporary string object

Avoiding self-assignment

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.

Always use empty() instead of size()==0

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.

A full std::string implementation

To see a complete version, you should check out some std::string implementations.