【玩转c++】string类的底层模拟实现

  •  本期主题:string类的模拟实现
  • 博客主页:小峰同学
  • 分享小编的在Linux中学习到的知识和遇到的问题
  • 小编的能力有限,出现错误希望大家不吝赐
  • 身为程序员,不会还有人没有女朋友吧。

直接上源码

#pragma once
#include<iostream>
#include<string>
#include<assert.h>
#include<string.h>
using namespace std;




namespace zxf
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

	public:
		/成员函数///
		//构造函数
		//string(const char* str = "\0")
		//string(const char* str = nullptr)
		//string(const char* str = NULL)
		//以上都是错误示范。
		string(const char* str = "")
		{
			//如果这里str位nullptr就是程序非法的
			if (nullptr == str)
			{
				assert(str);
				exit(1);
			}
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];//这里自己控制是否加一,根据capacity的具体意义确定。
			strcpy(_str, str);
		}

		//拷贝构造(古老写法)
		//string(const string& s)
		//{
		//	_size = s._size;
		//	_capacity = s._capacity;
		//	_str = new char[_capacity+1];
		//	//注意这里的+1,没有的话可能会报错。
		//	strcpy(_str, s._str);
		//}
		
		//拷贝构造(现代写法)
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{//注意这里的初始化列表不能少,不写后面tmp在析构的时候就会出问题。
			string tmp(s._str);
			//swap(tmp);
			this->swap(tmp);
			//是否加this指针都是在使用 自己的成员函数swap
		}

		//赋值重载(传统写法)
		//string& operator=(const string& s)
		//{
		//	if (this != &s)//这里用地址比较,不能用对象比较因为我们还没重载 == 号
		//	{
		//		char* pStr = new char[strlen(s._str) + 1];
		//		strcpy(pStr, s._str);
		//		delete[] _str;//要释放掉原来的空间。
		//		_str = pStr;
		//		_size = s._size;
		//		_capacity = s._capacity;
		//	}
		//	return *this;
		//	//if (this != &s)
		//	//{
		//	//	string tmp(s);
		//	//	swap(tmp);
		//	//}
		//	//return *this;
		//}
		//赋值重载(现代写法)
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}


		//析构函数
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
				_size = 0;
				_capacity = 0;
			}
		}
		iterator
		iterator begin()
		{
			return _str;
		}
		const_iterator begin()const
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator end()const
		{
			return _str + _size;
		}



		/get成员变量
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		char* c_str()const
		{
			return _str;
		}
		/其他函数
		void reserve(size_t capacity)
		{
			if(capacity>_capacity)//一般不会缩小容量。
			{
				_capacity = capacity;
				char* nstr = new char[_capacity + 1];
				strcpy(nstr, _str);
				delete[] _str;
				_str = nstr;
			}
		}

		void push_back(const char ch)
		{
			if (_size == _capacity)
			{
				//reserve(_capacity * 2);//考虑到空字符串的push_back,这里_capacity*2就会出问题。
				reserve(_capacity + 10);
				//也可也在这里判断一下是不是0,然后扩容
			}
			_str[_size++] = ch;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len >= _capacity)
			{
				reserve(_size + len * 2);
			}
			strcat(_str, str);
			_size += len;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}		
		string& operator+=(const char c)
		{
			push_back(c);
			return *this;
		}
	
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}

		bool empty()const
		{
			return 0 == _size;
		}

		//void resize(size_t newsize, char c = '\0')
		//{
		//	if (newsize < _size)
		//	{
		//		_size = newsize;
		//		_str[newsize] = '\0';
		//	}
		//	if (newsize < _capacity)
		//	{
		//		for (; _size <= newsize; _size++)
		//		{
		//			_str[_size] = c;
		//		}
		//		_size--;
		//	}
		//	else
		//	{
		//		reserve(newsize);
		//		for (; _size <= newsize; _size++)
		//		{
		//			_str[_size] = c;
		//		}
		//		_size--;
		//	}
		//}
		//优化方案
		void resize(size_t newSize, char c = '\0')
		{
			if (newSize > _size)
			{
				// 如果newSize大于底层空间大小,则需要重新开辟空间
				if (newSize > _capacity)
				{
					reserve(newSize);
				}
				memset(_str + _size, c, newSize - _size);
			}
			_size = newSize;
			_str[newSize] = '\0';
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		//注意重载一个const版本。
		//不然会存在一些潜在错误。
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}




		bool operator<(const string& s)
		{
			return strcmp(_str, s._str) < 0;
		}

		bool operator<=(const string& s)
		{
			return strcmp(_str, s._str) <= 0;
		}
		bool operator>(const string& s)
		{
			return strcmp(_str, s._str) > 0;
		}
		bool operator>=(const string& s)
		{
			return strcmp(_str, s._str) >= 0;
		}
		bool operator==(const string& s)
		{
			return strcmp(_str, s._str) == 0;
		}
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}


		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{
			assert(pos < _size);
			size_t begin = pos;

			while (begin < _size)
			{
				if (_str[begin] == c)
				{
					return begin;
				}
				begin++;
			}
			return npos;
		}
		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* pc = strstr(_str + pos, s);//借助C语言得strstr函数实现;
			if (pc == nullptr)
			{
				return npos;
			}
			else
			{
				return  pc - _str;
			}
		}
		// 删除pos位置上的元素,并返回该元素的下一个位置
		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len > _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				//strcpy(_str + pos, _str + pos + len);
				memmove(_str + pos, _str + pos + len, _size - pos - len + 1);
				//都是可以的 也可以自己控制挪动。
				_size -= len;
			}
			return *this;

		}





		string& insert(size_t pos, const char c)
		{
			assert(pos <= _size);

			if (_size == _capacity)
			{
				reserve(_capacity + 10);
			}

			//移动数据
			//size_t end = _size;
			//while (end >= pos)
			//{
			//	_str[end + 1] = _str[end];
			//	end--;
			//}//这样写pos ==0 的时候就会出问题。	
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end] = _str[end-1];
				end--;
			}//这样写也可以。
			//int end = _size;
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	end--;
			//}//可以这样改一下


			_str[pos] = c;
			_size++;
			_str[_size] = '\0';
			return *this;
		}

		string& insert(size_t pos, const char* str)
		{
			size_t len = strlen(str);
			if (_size + len >= _capacity)
			{
				reserve(_size + len * 2);
			}
			size_t end = _size;
			while (end > pos)//这里也是要注意这里控制好条件和考虑好 pos ==0情况。
				//这里情况很多种控制好就行。
			{
				_str[end + len-1] = _str[end-1];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			_str[_size] = '\0';
			return *this;
		}



		void swap(string& s)//交换函数,属于成员函数。
		{
			std::swap(s._str, _str);
			std::swap(s._size, _size);
			std::swap(s._capacity, _capacity);
		}




	private:
	    char*  _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos = -1;//一般静态成员变量初始化需要再类外面初始化,
		//但是有一个特例就是 const 修饰分静态成员变量,可以直接给缺省值初始化。
		//但是仅仅限于整型,
	};

	//注意参数的引用,和返回值的引用
	ostream& operator<<(ostream& out, const string& s)//<<的重载不一定非要实现成  友元函数。
	{
		//out << s.c_str();
		//这样写是不对的,防止'\0'的出现。
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		//一般会先清理
		s.clear();
		//
		//char ch;
		//in >> ch;
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	in >> ch;
		//}
		这样写会出现一个问题,istream 对象 cin 和scanf 相同  没办法取到  ' ' 和 '\n' 
		scanf 和 cin 只能取到非 ' ' 和 '\n' 的字符。所以这个循环位死循环。

		//char ch = in.get();
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	ch = in.get();
		//}
		//istream类对象的成员函数 get()。
		//in.get()是按照逐个字符逐个字符读取,包含 ' ' 和 '\n' 。 和cin,scanf不同。
		//这样也会照成频繁扩容,对性能会有一定的影响。


		//库里面是这样定义的。 //一段一段的 +=
		char buff[128] = { '\0' };
		size_t i = 0;
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			//i 是 0 ~ 126一共存 127个字符。
			if (i < 127)//这里需要留一个字符,保存'\0'
			{
				buff[i++] = ch;
			}
			else{
				s += buff;
				i = 0;
				buff[i++] = ch;
			}
			ch = in.get();
		}
		if (i > 0){
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}


	istream& getline(istream& in, string& s)
	{
		s.clear();
		char buff[128] = { '\0' };
		size_t i = 0;
		char ch = in.get();
		while (ch != '\n')//这里比 cin>> 少一个 ch != ‘ ’ 。
		{
			//i 是 0 ~ 126一共存 127个字符。
			if (i < 127)//这里需要留一个字符,保存'\0'
			{
				buff[i++] = ch;
			}
			else {
				s += buff;
				i = 0;
				buff[i++] = ch;
			}
			ch = in.get();
		}
		if (i > 0) {
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

	//这是一些测试用例。
	void test1()
	{
		string s1("zhang");
		string s2(s1);
	}
	void test2()
	{
		string s1("zhang");
		string s2 = "xue";
		cout << s2.c_str() << endl;
		s2 = s1;
		cout<< s2.c_str() << endl;
	}
	void test3()
	{
		string s1("111111");
		const string s2(s1);


		string::iterator first1 = s1.begin();
		string::iterator last1 = s1.end();
		while (first1 != last1)
		{
			cout << *first1;
			first1++;
		}
		cout << endl;
		string::const_iterator first2 = s2.begin();
		string::const_iterator last2 = s2.end();
		while (first2 != last2)
		{
			cout << *first2;
			first2++;
		}
		cout << endl;

		//范围for的底层就是迭代器,当我们按照底层一样的名称相同
		for (auto& ch : s1)
		{
			ch++;
		}

		cout << s1.c_str() << endl;//222222

		for (auto& ch : s2)
		{
			cout << ch << endl;
		}

	}
	void test4()
	{
		string s1("zhang");
		cout << s1.size() << endl;
		cout << s1.capacity() << endl;

		const string s2("xue");
		cout << s2.size() << endl;
		cout << s2.capacity() << endl;

	}
	void test5()
	{
		string s1("zhang");
		string s2;
		s1 += "xuefeng";
		s2 += "zhangxuefeng";
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}
	void test6()
	{
		string s1("zhang");
		s1 += 'x';
		s1 += 'u';
		s1 += 'e';
		s1 += 'f';
		s1 += 'e';
		s1 += 'n';
		s1 += 'g';
		cout << s1.c_str() << endl;
	}
	void test7()
	{
		const string s1("zhang");
		size_t size = s1.size();
		//cout << size << endl;
		//char& ch = s1[3];
		const char& ch = s1[3];
		//ch++;
		cout << s1.c_str() << endl;
	}
	void test8()
	{

		string s1("zhang");
		s1.insert(3, 'z');
		s1.insert(3, 'z');
		s1.insert(3, 'z');
		s1.insert(3, 'z');
		s1.insert(0, 'z');
		s1.insert(0, 'z');
		s1.insert(0, 'z');
		s1.insert(0, 'z');
		cout << s1.c_str() << endl;
		string s2("zhang");
		s2.insert(0, "zhang");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "xue");
		s2.insert(5, "xue");
		s2.insert(0, "xue");
		s2.insert(0, "");
		s2.insert(5, "");
		s2.insert(5, "");
		cout << s2.c_str() << endl;
	}
	void test9()
	{
		string s1("zhang");
		string s2("zhang");
		string s3("zhan");

		cout << (s1 == s2) << endl;//t
		cout << (s1 != s2) << endl;//f
		cout << (s1 == s3) << endl;//f
		cout << (s1 != s3) << endl;//t

	}
	void test10()
	{
		//string s1("zhangxuefeng");
		//s1.erase(0, 5);
		//cout << s1.c_str() << endl;

		//string s1("zhangxuefeng");
		//size_t pos = s1.find("xue");
		//s1.erase(pos, 3);
		//size_t pos2 = s1.find('z');
		//s1.erase(pos2, 1);
		//cout << s1.c_str() << endl;

		//string s1("zhang");
		//cout << s1 << endl;


		string s1("zhang");
		cin >> s1;
		cout << s1;

		string s2("zhang");
		getline(cin, s2);
		cout << s2;
	}
}

这里我就不一一讲解了 ,注释的很清楚。

猜你喜欢

转载自blog.csdn.net/zxf123567/article/details/128459872