[C++]:9: string类的模拟实现

string类的模拟实现

  • 0:类的成员变量:
  • 一.构造析构赋值:
    • 1.构造:
    • 2.析构:
    • 3.赋值:
  • 二.容量相关:
    • 1.size()
    • 2.capacity()
    • 3.reserve()
    • 4.clear()
    • 5.empty()
  • 三.c_str和operator[]
    • 1.c_str
    • 2.operator[]
  • 四.push_back 和 append 和 operator+=
    • 1.push_back:
    • 2.append:
    • 3.operator+=:
      • string& operator+= (const string& str);
      • string& operator+= (const char* s);
      • string& operator+= (char c);
  • 五:insert 和 erase 和 swap
    • 1.insert:
      • 1-1: 在pos位置插入单个字符:
      • 1-2:在pos位置插入const char* 数据:
    • 2.erase:
    • 3.swap:
  • 六. find() 和 substr()
    • 1.find()
      • 1-1:size_t find (const char* s, size_t pos = 0) const;
      • 1-2:size_t find (char c, size_t pos = 0) const;
    • 2.substr()
  • 七:iterator:
  • 八:流插入和流提取:
    • 流插入:
    • 流提取:

0:类的成员变量:

[C++]:9: string类的模拟实现_第1张图片

//测试:遍历用来测试代码:
	void print_str()
	{
		assert(_str != nullptr);
		for (size_t pos = 0; pos < _size; pos++)
		{
			std::cout << _str[pos];
		}
		std::cout << std::endl;
	}

一.构造析构赋值:

1.构造:

[C++]:9: string类的模拟实现_第2张图片

1.我们观察一下实现1和4可以实现一个缺陷省参数去把两个构造给包含在一起:

//1-1:空和const char* s
	string(const char* str="\0")//const char* str = "" C语言特性空字符串就是一个"\0"
	{
		//问题一:建议不要使用初始化列表进行:因为初始化列表的顺序是根据成员变量声明的顺序。
		//问题二:应该开辟的空间大小?
		 
		//_size 的意义是:当前有效字符的个数 _capacity的意义是:当前可以存放的最多的有效字符的个数:

		if (str == "\0")
		{
			_str = new char[1] {'\0'};
			_size = 0;
			_capacity = 0;
		}
		else
		{
			size_t len = strlen(str);
			_str = new char[len+1];
			strcpy(_str, str);
			_size = len;
			_capacity = len;
		}
	}

2.2是我们的拷贝构造是需要实现的!

1.传统写法:自己去开辟空间自己完成这个拷贝

string(const string& str)
	{
		if (str._size == str._capacity)
		{
			if (str._capacity == 0)
			{
				_str = new char[1] {'\0'};
				_size = 0;
				_capacity = 0;
			}
			else
			{
				_str = new char[str._capacity + 1];

				for (int i = 0; i < str._size; i++)
				{
					_str[i] = str._str[i];
				}

				_size = str._size;
				_capacity = str._capacity;

			}
		}
	}

2.现代写法:调用构造函数并且交换实现这个构造函数!

[C++]:9: string类的模拟实现_第3张图片

	//1-2-1:拷贝构造(现代写法):

	string(string& str)
	{
		string s1(str._str);
		(*this).swap(s1);
	}

3.6是使用n个C字符实现去构造一个字符串也可以实现一下:

string(size_t n, const char c)
	{
		if (n == 0)
		{
			_str = new char[1] {'\0'};
			_size = 0;
			_capacity = 0;
		}
		else
		{
			_str = new char[n + 1];
			size_t pos = 0;
			for (pos = 0; pos < n; pos++)
			{
				_str[pos] = 'c';
			}
			_str[pos] = '\0';
			_size = n;
			_capacity = n;
		}
	}

2.析构:

//2:析构函数:

	~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}

3.赋值:

[C++]:9: string类的模拟实现_第4张图片

1.string 类型的赋值:(传统写法)

//2-1:字符串赋值:
	string& operator=(const string& str)
	{
		//1.排除自己给自己赋值的情况!
		if (this == &str)
			return *this;

		//2.考虑str比较长的情况:
		if (str._size > _capacity)
		{
			//1.开一个空间:
			char* tmp = new char[str._size + 1];
			//2.拷贝数据到新空间:
			strcpy(tmp, str._str);
			//3.释放旧空间:
			delete[] _str;
			_str = tmp;
			_size = str._size;
			_capacity = str._size;
		}
		//3.被赋值的变量的_capacity是足够的!
		else
		{
			strcpy(_str, str._str);
			_size = str._size;

		}

		return *this;
	}

1-1.string 类型的赋值:(现代写法)

//2-2:字符串赋值(现代赋值):
	string& operator=(const string& str)
	{
		string s1(str._str);
		swap(s1);

		return *this;
	}

2.const char* 类型的赋值:(传统写法)

//2-2:const char* 赋值:
	string& operator=(const char* str)
	{
		size_t len = strlen(str);

		//2.考虑str比较长的情况:
		if (len > _capacity)
		{
			//1.开一个空间:
			char* tmp = new char[len + 1];
			//2.拷贝数据到新空间:
			strcpy(tmp, str);
			//3.释放旧空间:
			delete[] _str;
			_str = tmp;
			_size = len;
			_capacity = len;
		}

		//3.被赋值的变量的_capacity是足够的!
		else
		{
			strcpy(_str, str);
			_size = len;

		}

		return *this;
	}

二.容量相关:

1.size()

[C++]:9: string类的模拟实现_第5张图片

//3-1:size():不对对象进行修改只去读数据的代码给*this加一个const修饰! 
	size_t size()const
	{
		return _size;
	}

2.capacity()

[C++]:9: string类的模拟实现_第6张图片

//3-2: capacity()
	size_t capacity()const
	{
		return _capacity;
	}

3.reserve()

[C++]:9: string类的模拟实现_第7张图片

//3-3:reserve():n为多大就开多大的有效空间:
	void reserve(size_t n)
	{
		//1.不考虑容量减少的情况:

		//2.C++ 下是没有realloc函数:

		//2-1:开一个大小为n的新的空间:
		char* tmp = new char[n+1];
		//2-2:把字符串用来的内容放到新开辟好的空间里面:
		strcpy(tmp, _str);
		//2-3:原来空间释放+数值的更新:
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}

4.clear()

[C++]:9: string类的模拟实现_第8张图片

//3-4:clear():擦除字符串内容+把字符串变成空字符串
	void clear()
	{
		delete[] _str;
		_str = new char[1] {'\0'};
		_size = 0;
		_capacity = 0;
	}

5.empty()

[C++]:9: string类的模拟实现_第9张图片

//3-5:empty():判断字符串是否为空:
	bool empty()
	{
		if (_size == 0)
		{
			return true;
		}
		return false;
	}

三.c_str和operator[]

1.c_str

//4-1:c_str():
	const char* c_str()const
	{
		return _str;
	}

2.operator[]

//4-2:operator[]

	//4-2-1:只读:

	//const string * this 修饰内容:
	
	//1.引用返回造就了可读可写!
	//2.双const修饰防止了引用返回导致的修改的可能性!

	const char& operator[](size_t pos)const
	{
		assert(_str != nullptr);
		return _str[pos];
	}


	//4-2-2:可读可写:
	
	//1.引用返回造就了可读可写!
	char& operator[](size_t pos)
	{
		assert(_str != nullptr);
		return _str[pos];
	}

四.push_back 和 append 和 operator+=

1.push_back:

[C++]:9: string类的模拟实现_第10张图片

//5-1:push_back()

	void push_back(char c)
	{
		assert(_str != nullptr);

		if (_size == _capacity)
		{
			//1.处理开始为0的情况,正常就去扩二倍:
			_capacity = _capacity == 0 ? 4 : _capacity*2;
			//2.使用reserve非常方便:
			reserve(_capacity);
		}

		//3.要保持住结尾的'\0'
		_str[_size++] = c;
		_str[_size] = '\0';

	}

2.append:

[C++]:9: string类的模拟实现_第11张图片

1.加上一个字符串:

	//5-2-1:添加一个字符串:
	string& append(const string& str)
	{
		assert(_str != nullptr);
		size_t len = str.size();

		if (len + _size > _capacity)
		{
			reserve(len + _size + 1);
		}

		strcpy(_str+_size, str._str);
		_size += str._size;
		return *this;
	}

2.加上一个const char*

//5-2-2:添加一个const char* 的值:

	string& append(const char* str)
	{
		assert(_str != nullptr);
		size_t len = strlen(str);

		if (len + _size > _capacity)ize
		{
			reserve(len + _size + 1);
		}
		strcpy(_str + _size, str);
		_size += len;
		return *this;
	}

3.operator+=:

[C++]:9: string类的模拟实现_第12张图片

string& operator+= (const string& str);

string& operator+= (const string& str)
	{
		append(str);
		return *this;
	}

string& operator+= (const char* s);

string& operator+= (char c)
	{
		push_back(c);
		return *this;
	}

string& operator+= (char c);

string& operator+= (const char* s)
	{
		append(s);
		return *this;
	}

五:insert 和 erase 和 swap

1.insert:

[C++]:9: string类的模拟实现_第13张图片

1-1: 在pos位置插入单个字符:

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

		if (_size + 1 > _capacity)
		{
			reserve(_size + 1);
		}

		size_t end = _size + 1;

		while (pos < end)
		{
			_str[end] = _str[end - 1];
			end--;
		}

		_str[pos] = c;

		_size += 1;
		_str[_size] = '\0';

		return *this;
	}

1-2:在pos位置插入const char* 数据:

string& insert(size_t pos, const char* str)
	{
		assert(pos < _size);

		size_t len = strlen(str);

		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}

		size_t end = _size + len;

		while (pos < end)
		{
			_str[end] = _str[end - len];
			end--;
		}

		strncpy(_str + pos, str,len);

		_size += len;
		_str[_size] = '\0';

		return *this;
	}

2.erase:

[C++]:9: string类的模拟实现_第14张图片

	string& erase(size_t pos = 0, size_t len = npos)
	{
		assert(_str != nullptr);

		if (len == npos || len > _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
		}
		return *this;
	}

3.swap:

[C++]:9: string类的模拟实现_第15张图片

1.swap 为什么会有三个重载:
2.string 类实现的swap方法:
3.swap一个全局的:
4.使用全局函数模板生成的一个string

void swap(string& s1, string& s2)
	{
		char* tmp = s1._str;
		size_t tmp_1 = s1._size;
		size_t tmp_2 = s2._capacity;

		s1._str= s2._str;
		s1._size= s2._size;
		s1._capacity= s2._capacity;

		s2._str = tmp;
		s2._size = tmp_1;
		s2._capacity = tmp_2;
	}

	//6-3:swap()
	void swap(string& str)
	{
		swap(*this, str);
	}

六. find() 和 substr()

1.find()

[C++]:9: string类的模拟实现_第16张图片

1-1:size_t find (const char* s, size_t pos = 0) const;

size_t find(const char* ch, size_t pos = 0)
	{
		assert(pos < _size);

		//1.有可能找不到子串:
		const char* ptr = strstr(_str, ch);

		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}

	}

1-2:size_t find (char c, size_t pos = 0) const;

//7:find() substr()
	size_t pos(char ch , size_t pos = 0)
	{
		assert(pos < _size);

		for (size_t pos = 0; pos < _size; pos++)
		{
			if (_str[pos] == ch)
			{
				return pos;
			}
		}
	}

2.substr()

[C++]:9: string类的模拟实现_第17张图片

string substr(size_t pos , size_t len=npos)
	{
		string str;
		if (len == npos || len > _size-pos)
		{
			str.reserve(_size - pos);
		}

		for (size_t i = pos; i < _size; i++)
		{
			str += _str[i];
		}

		return str;
	}

七:iterator:

[C++]:9: string类的模拟实现_第18张图片

1.begin()

//8:iterator()

	typedef char* iterator;

	iterator begin()
	{
		return _str;
	}

2.end()

iterator end()
	{
		return _str + _size;
	}

3.使用:

void text_8()
{
	sfpy::string s1("hello iterator");

	sfpy::string::iterator it_1 = s1.begin();

	while (it_1!=s1.end())
	{
		std::cout << (*it_1);
		it_1++;
	}

}

八:流插入和流提取:

流插入:

[C++]:9: string类的模拟实现_第19张图片
方法一:

1.使用scanf获取标准输入(stdin)中的数据。
2.C语言中的标准输入和C++ 的标准输入是不一样的!
3.C++ 强制去兼容C语言在这个地方!
4.这个地方获取一个字符串可以获取到空格的版本!
5.获取数据之前是需要清空这个空间因为我们是一个赋值!
[C++]:9: string类的模拟实现_第20张图片

1.我们自己实现一个clear:
2.我们自己构造一个空串的时候都需要一个\0 。
[C++]:9: string类的模拟实现_第21张图片

std::istream& operator>>(std::istream& _cin, string& s1)
{
	//assert(string::_str != nullptr);
	s1.clear();
	
	char tmp = 0;
	while (scanf("%c", &tmp) != 0 && tmp != '\n')
	{
		s1.push_back(tmp);
	}
	return _cin;
}

方法二:优化1

1.这里我们可以去使用C++ 的输入!
2.关于 cin>>ch 其实是获取不了空格和\n
3.获取不了\n和空格是结束不了这一次的字符串输入:
4.在cin中有一个方法get() 方法!

std::istream& operator>>(std::istream& _cin, string& s1)
{
	//assert(string::_str != nullptr)
	s1.clear();
	//从缓冲区一个一个读从缓冲区到文件中!
	char tmp = _cin.get();
	while (tmp != ' ' && tmp != '\n')
	{
		s1 += tmp;
		tmp = _cin.get();
	}

	return _cin;
}

方法二:优化2

1.在这个地方我们使用operator+=(operator+= 底层使用判断空间大小和进行扩容每次扩容二倍) 。
2.如果输入一个比较长的字符串那么肯定会存在多次扩容降低了效率!
3.如果开始就开比较大的空间那么如果需要的空间并不大那么空间浪费如果字符非常多相当于这样的开始开比较大的空间的用处也不是很大!
4.在函数中定义一个字符数组确定一个大小输入数据不直接往字符串中输入而是先输入到字符数组中!

std::istream& operator>>(std::istream& _cin, string& s1)
{
	//assert(string::_str != nullptr)
	s1.clear();

	char arr[128] = {0};
	int i = 0;
	//从缓冲区一个一个读从缓冲区到文件中!
	arr[i] = _cin.get();

	while ( arr[i]!= ' ' && arr[i] != '\n')
	{
		i++;
		arr[i] = _cin.get();

		if (i == 126)
		{
			arr[125] = '\0';
			s1 += arr;
			i = -1;
		}
	}

	if (i>=0)
	{
		arr[i] = '\0';
		s1 += arr;
	}

	return _cin;
}

[C++]:9: string类的模拟实现_第22张图片

流提取:

[C++]:9: string类的模拟实现_第23张图片

std::ostream& operator<<(std::ostream& _cout, string& s1)
{
	//assert(string::_str != nullptr);

	string::iterator it_1 = s1.begin();

	while (it_1 != s1.end())
	{
		_cout << (*it_1);
		it_1++;
	}
	return _cout;
}

你可能感兴趣的:(c++,java,开发语言)