string类是C++的一个常见的面试题,考查面试者的基本功,虽然简单,但是有很多细节需要注意。


#pragma once
#include <assert.h>

/*
	深拷贝与浅拷贝:
        浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,
	而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。浅拷贝是指源对象
	与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。
	    深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
	    举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿
	还是李四缺胳膊少腿都不会影响另外一个人。
*/

class String
{
	friend ostream& operator<<(ostream& _cout, const String& s);
public:
	/*
	String()
		:_pStr(/ *new char('\0')* /new char[1])
	{
		_pStr[0] = '\0'; //不能是"\0"
	}

	//new和delete,new[]和delete[]一定要匹配使用
	//strlen 不能传NULL
	
	String(char* pStr)
		:_pStr(new char[strlen(pStr)+1])
	{
		strcpy(_pStr, pStr); //strcpy会拷贝'\0'过去
	}
	*/

	//构造函数合并
	String(char* pStr = "")
	{
		_size = strlen(pStr);
		_capacity = _size+1;
		_pStr = new char[_size+1];
		strcpy(_pStr, pStr);
	}

	/*
	//拷贝构造函数常规写法
	String(const String& s)
		:_pStr(new char[strlen(s._pStr)+1])
	{
		strcpy(_pStr, s._pStr);
	}
	*/

	String(const String& s)
		:_pStr(NULL) //需要先将this->_pStr置为空,否则交换后tmp为随机值,析构tmp时delete[]出现错误
	{
		String tmp(s._pStr);
		std::swap(_pStr, tmp._pStr);
	}

	/*
	//赋值运算符重载常规写法 -> 释放原来的空间,开辟和s一样大的空间
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			/ *
			//存在一致性问题。如果new空间失败,原来的空间将被释放,深拷贝失败
			delete[] _pStr;
			_str = new char[strlen(s._pStr)+1];
			strcpy(_pStr, s._pStr);
			* /

			//先开辟空间,再拷贝
			char* tmp = new char[strlen(s._pStr)+1];
			strcpy(tmp, s._pStr);
			delete[] _pStr;
			_pStr = tmp;
		}

		return *this;
	}
	*/

	/*
	//赋值运算符重载现代写法
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String tmp(s._pStr);
			std::swap(_pStr, tmp._pStr);
		}

		return *this;
	}
	*/

	//赋值运算符重载现代写法
	String& operator=(String s)
	{
		swap(_pStr, s._pStr);

		return *this;
	}

	~String()
	{
		if (_pStr)
			delete[] _pStr;
	}
	
	char* C_Str()
	{
		return _pStr;
	}

	char& operator[](size_t index)
	{
		return _pStr[index];
	}

	void PushBack(const char& ch)
	{
		/*_CheckCapacity(_size + 2);

		_pStr[_size] = ch;
		_pStr[++_size] = '\0';*/

		Insert(_size, ch);
	}

	void PopBack()
	{
		assert(_size > 0);
		--_size;
		_pStr[_size] = '\0';
	}

	void Insert(size_t pos, char ch)
	{
		assert(pos <= _size);

		_CheckCapacity(_size + 2);

		size_t cur = _size;
		while (cur >= pos)
		{
			_pStr[cur+1] = _pStr[cur];
			cur--;
		}
		_pStr[pos] = ch;

		++_size;
	}

	void Insert(int pos, const char* str)
	{
		assert(pos>=0 && pos <= _size);

		//增容
		size_t size = strlen(str);
		_CheckCapacity(_size+1+size);

		//挪动数据
		int index = _size;
		while (index >= pos)
		{
			_pStr[index+size] = _pStr[index];
			--index;
		}

		//拷贝
		for (size_t i = 0; i < size; ++i)
		{
			_pStr[pos++] = str[i];
		}

		_size += size;
	}

	int Find(char ch)
	{
		for (size_t index = 0; index < _size; ++index)
		{
			if (_pStr[index] = ch)
			{
				return index;
			}
		}

		return -1;
	}

	int Find(const char* subStr)
	{
		assert(subStr);

		size_t dstIndex = 0; //要查找的子串
		size_t srcIndex = 0;
		size_t subSize = strlen(subStr);
		for (dstIndex = 0; dstIndex <= _size - subSize; ++dstIndex)
		{
			for (srcIndex = 0; srcIndex < subSize; ++srcIndex)
			{
				if (subStr[srcIndex] != _pStr[dstIndex+srcIndex])
				{
					break;
				}
			}

			if (srcIndex == subSize)
			{
				return dstIndex;
			}
		}

		return -1;
	}

	void Erase(size_t pos)
	{
		assert(pos < _size);

		for (size_t i = pos+1; i <= _size; ++i)
		{
			_pStr[i-1] = _pStr[i];
		}

		--_size;
	}

	String& operator+=(const String& s)
	{
		Insert(_size, s._pStr);

		return *this;
	}
	//String s("Hello");
	//可以使用 s += String(" World"); String(" World") -> 拷贝构造 -> String& s
	//也可使用 s += " World";  " World" -> 构造函数 -> String& s

	//如果改为String& operator+=(String& s)则不能使用 s += " World"。 例如:
	/*
	int i = 0;
	//double& a = i; //i先创建一个临时对象,然后把临时对象给double& a,a就成为一个临时变量的引用。错误
	const double& a = i; //加const,可以引用临时变量。正确
	*/

	/*
	String s1 = "sssssss"; //"sssssss"生成一个匿名对象,匿名对象再构造s1。编译器将两步优化为一步:"sssssss"直接构造s1。
	//String& s2 = "sssssss"; //不能引用匿名(临时)对象。错误
	const String& s3 = "sssssss"; //加const,可以引用临时变量。正确
	*/

	bool operator==(const String& s)
	{
		return (strcmp(_pStr, s._pStr) == 0);
	}

	bool operator>(const String& s)
	{
		return (strcmp(_pStr, s._pStr) > 0);
	}

	bool operator>=(const String& s)
	{
		return (*this > s || *this == s);
	}

	bool operator<(const String& s)
	{
		return !(*this >= s);
	}

	bool operator<=(const String& s)
	{
		return !(*this > s);
	}

protected:
	void _CheckCapacity(size_t needSize)
	{
		if (_size+1 >= _capacity)
		{
			_capacity = _capacity+needSize > 2*_capacity ? needSize : 2*_capacity;
			_pStr = (char*)realloc(_pStr, _capacity);
		}
	}
	
protected:
	char* _pStr;
	size_t _size;
	size_t _capacity;
};

ostream& operator<<(ostream& _cout, const String& s)
{
	_cout<<s._pStr;
	return _cout;
}


推荐文章:

C++面试中string类的一种正确写法:http://coolshell.cn/articles/10478.html