C++ String Class/Data Structure

The C++ string class is a very valuable addition to the C++ library. Every wonder how they did it? Here is an example string class. It takes strings, String objects, or numbers which are converted to strings. It copies, assigns and concatenates strings, String objects and numbers. It is very fast taking advantage of the 32-bit or 64-bit registers of a computer and not copying data one byte at a time (when copying from another String object). A String object only consumes 4 bytes of stack space on 32-bit systems and 8 bytes on 64-bit systems.

Don't confuse String with string.

Supports both char for ASCII and wchar_t for UNICODE strings.

// String.h
#ifndef STRING_H
#define STRING_H

typedef char CHAR; // use "wchar_t" for UNICODE or "char" for ASCII

void copy_16bit(CHAR *dest, const CHAR *source, const unsigned int length)
{
	unsigned int limit = length >> 1 << 1; // 2^1 is 2
	unsigned int i;
	for (i = 0; i < limit; i += sizeof(void*) / sizeof(CHAR)) // 2 bytes divided by 1 or 2
		*((short*)(dest + i)) = *((short*)(source + i)); // there is no benefit if copying UNICODE string on 16 bit system, but UNICODE is rare for a 16-bit system
	for (; i < length; i++)
		dest[i] = source[i];
	dest[i] = '\0';
}

void copy_32bit(CHAR *dest, const CHAR *source, const unsigned int length)
{
	unsigned int limit = length >> 2 << 2; // 2^2 is 4
	unsigned int i;
	for (i = 0; i < limit; i += sizeof(void*) / sizeof(CHAR)) // 4 bytes divided by 1 or 2
		*((int*)(dest + i)) = *((int*)(source + i));
	for (; i < length; i++)
		dest[i] = source[i];
	dest[i] = '\0';
}

void copy_64bit(CHAR *dest, const CHAR *source, const unsigned int length)
{
	unsigned int limit = length >> 3 << 3; // 2^3 is 8
	unsigned int i;
	for (i = 0; i < limit; i += sizeof(void*) / sizeof(CHAR)) // 8 bytes divided by 1 or 2
		*((long long*)(dest + i)) = *((long long*)(source + i));
	for (; i < length; i++)
		dest[i] = source[i];
	dest[i] = '\0';
}


class StringData
{
public:
	CHAR *array;
	unsigned int max;
	unsigned int length;
	void(*copy_string)(CHAR *dest, const CHAR *source, const unsigned int length);
	StringData(unsigned int buffer_size) : length(0), max(buffer_size)
	{
		copy_string = (sizeof(void*) == 8 ? copy_64bit : (sizeof(void*) == 4 ? copy_32bit : copy_16bit));
		array = new CHAR[max];
		array[0] = '\0';
	}
	~StringData()
	{
		delete[]array;
		array = nullptr;
	}
};

class String
{
private:
	StringData *sd;
	void number_to_string(unsigned long number, CHAR *buffer, const bool negative)
	{
		CHAR *first;
		if (negative)
		{
			*buffer++ = '-';
			number = (unsigned long)(-(long)number);
		}
		first = buffer;
		do
		{
			unsigned digit = (unsigned)(number % 10);
			number /= 10;
			*buffer++ = (CHAR)(digit + '0');
		} while (number > 0);
		*buffer-- = '\0';
		do
		{
			CHAR temp = *buffer;
			*buffer = *first;
			*first = temp;
			--buffer;
			++first;
		} while (first < buffer);
	}
public:
	String()
	{
		sd = new StringData(1024);
	}
	String(const long source)
	{
		sd = new StringData(1024);
		this->operator=(source);
	}
	String(const unsigned long source)
	{
		sd = new StringData(1024);
		this->operator=(source);
	}
	String(const int source)
	{
		sd = new StringData(1024);
		this->operator=(source);
	}
	String(const unsigned int source)
	{
		sd = new StringData(1024);
		this->operator=(source);
	}
	String(const CHAR *source)
	{
		sd = new StringData(1024);
		this->operator=(source);
	}
	String(const String &obj)
	{
		sd = new StringData(obj.sd->max);
		this->operator=(obj);
	}
	String(const CHAR ch)
	{
		sd = new StringData(1024);
		this->operator=(ch);
	}
	~String()
	{
		delete sd;
		sd = nullptr;
	}
	const CHAR *string() const
	{
		return sd->array;
	}
	unsigned int length() const
	{
		return sd->length;
	}
	unsigned int buffer_size() const
	{
		return sd->max * sizeof(CHAR);
	}
	unsigned int buffer_length() const
	{
		return sd->max;
	}
	const String &operator=(const String &obj)
	{
		if (this != &obj)
		{
			if (obj.sd->length >= sd->max) // then increase array size
			{
				sd->max = obj.sd->max;
				delete[]sd->array;
				sd->array = new CHAR[sd->max];
			}
			sd->copy_string(sd->array, obj.sd->array, obj.sd->length);
			sd->length = obj.sd->length;
		}
		return (*this);
	}
	void operator+=(const String &obj)
	{
		if (obj.sd->length >= sd->max - sd->length) // then increase array size
		{
			sd->max = (obj.sd->length + sd->length) << 1; // give it a little bit of a buffer (2 times length of new string)
			CHAR *new_array = new CHAR[sd->max];
			sd->copy_string(new_array, sd->array, sd->length);
			delete[]sd->array;
			sd->array = new_array;
		}
		sd->copy_string(&sd->array[sd->length], obj.sd->array, obj.sd->length);
		sd->length += obj.sd->length;
	}
	void operator+=(const CHAR *source)
	{
		unsigned int i, j;
		for (i = sd->length, j = 0; source[j]; )
		{
			sd->array[i++] = source[j++];
			if (i == sd->max) // then increase array size
			{
				sd->max <<= 1;
				CHAR *new_array = new CHAR[sd->max];
				unsigned int x;
				for (x = 0; x < i; x++)
					new_array[x] = sd->array[x];
				new_array[x] = '\0';
				delete[]sd->array;
				sd->array = new_array;
			}
		}
		sd->array[i] = '\0';
		sd->length = i;
	}
	void operator+=(const CHAR ch)
	{
		CHAR sz[2] = { ch, '\0' };
		this->operator+=(sz);
	}
	const CHAR *operator=(const CHAR *source)
	{
		sd->length = 0;
		this->operator+=(source);
		return source;
	}
	CHAR operator=(const CHAR ch)
	{
		sd->length = 0;
		this->operator+=(ch);
		return ch;
	}
	long operator=(const long number)
	{
		CHAR sz[12];
		number_to_string(number, sz, number < 0);
		this->operator=(sz);
		return number;
	}
	void operator+=(const long number)
	{
		CHAR sz[12];
		number_to_string(number, sz, number < 0);
		this->operator+=(sz);
	}
	unsigned long operator=(const unsigned long number)
	{
		CHAR sz[12];
		number_to_string(number, sz, false);
		this->operator=(sz);
		return number;
	}
	void operator+=(const unsigned long number)
	{
		CHAR sz[12];
		number_to_string(number, sz, false);
		this->operator+=(sz);
	}
	int operator=(const int number)
	{
		this->operator=((long)number);
		return number;
	}
	void operator+=(const int number)
	{
		this->operator+=((long)number);
	}
	unsigned int operator=(const unsigned int number)
	{
		this->operator=((unsigned long)number);
		return number;
	}
	void operator+=(const unsigned int number)
	{
		this->operator+=((unsigned long)number);
	}
	bool operator==(const CHAR *str) const
	{
		unsigned int i = 0;
		CHAR *a = sd->array;
		for (i = 0; a[i] && str[i] && a[i] == str[i]; i++);
		return(a[i] == str[i]);
	}
	bool operator==(const String &obj) const
	{
		return this->operator==(obj.sd->array);
	}
	bool operator==(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return this->operator==(sz);
	}
	bool operator!=(const String &obj) const
	{
		return !this->operator==(obj.sd->array);
	}
	bool operator!=(const CHAR *str) const
	{
		return !this->operator==(str);
	}
	bool operator!=(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return !this->operator==(sz);
	}
	bool operator>(const CHAR *str) const
	{
		unsigned int i = 0;
		CHAR *a = sd->array;
		for (i = 0; a[i] && str[i] && a[i] == str[i]; i++);
		return(a[i] > str[i]);
	}
	bool operator>(const String &obj) const
	{
		return this->operator>(obj.sd->array);
	}
	bool operator>(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return this->operator>(sz);
	}
	bool operator<(const CHAR *str) const
	{
		unsigned int i = 0;
		CHAR *a = sd->array;
		for (i = 0; a[i] && str[i] && a[i] == str[i]; i++);
		return(a[i] < str[i]);
	}
	bool operator<(const String &obj) const
	{
		return this->operator<(obj.sd->array);
	}
	bool operator<(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return this->operator<(sz);
	}
	bool operator>=(const CHAR *str) const
	{
		unsigned int i = 0;
		CHAR *a = sd->array;
		for (i = 0; a[i] && str[i] && a[i] == str[i]; i++);
		return(a[i] >= str[i]);
	}
	bool operator>=(const String &obj) const
	{
		return this->operator>=(obj.sd->array);
	}
	bool operator>=(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return this->operator>=(sz);
	}
	bool operator<=(const CHAR *str) const
	{
		unsigned int i = 0;
		CHAR *a = sd->array;
		for (i = 0; a[i] && str[i] && a[i] == str[i]; i++);
		return(a[i] <= str[i]);
	}
	bool operator<=(const String &obj) const
	{
		return this->operator<=(obj.sd->array);
	}
	bool operator<=(const CHAR ch) const
	{
		CHAR sz[2] = { ch, '\0' };
		return this->operator<=(sz);
	}
	CHAR operator[](const unsigned int index) const // should enter an index value less than length(); length == '\0'
	{
		return sd->array[index];
	}
	const CHAR *operator>>(const unsigned int index) // returns the string at the given index
	{
		if (index < sd->length)
			return &sd->array[index];
		return &sd->array[sd->length];
	}
};

const CHAR *operator*(const String &obj)
{
	return obj.string();
}

#endif
#include <iostream>
#include "String.h"
using namespace std;

int main()
{
	String str = "abc";
	cout << str.string() << endl;
	str += "def";
	cout << *str << endl; // * is alternate to using str.string()
	String str2 = str;
	if (str == str2)
		cout << str.string() << " equal to " << str2.string() << endl;
	if (str != "bcdef")
		cout << str.string() << " not equal to bcdef" << endl;
	if (str > 'a')
		cout << str.string() << " greater than 'a'" << endl;
	cout << str2.string() << endl;
	str += str2;
	cout << str.string() << endl;
	str += 1827374665; // numbers are converted to strings
	cout << *str << endl;
	str2 = 2333222111;
	cout << *str2 << endl;
	cout << (str2 >> 3) << endl; // returns 3222111 (str2 starting at character index 3)
	str2 = str2 >> 1; // str2 equals itself starting at character index 1
	cout << *str2 << endl;
	str2 += -776655;
	cout << *str2 << endl;
	return 0;
}

Coding Video

https://youtu.be/ehB_6T0Mo0Y