首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入剖析String类的底层实现原理

深入剖析String类的底层实现原理

作者头像
夜雨声烦1413
发布2026-01-12 16:03:21
发布2026-01-12 16:03:21
910
举报

嘿嘿,家人们,今天咱们来模拟实现string,好啦,废话不多讲,开干!


1:string.h

1.1:构造函数与拷贝构造函数

1.1.1:写法一
代码语言:javascript
复制
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;

//避免与库中的string冲突,因此使用命名空间
namespace MyString
{
	class string
	{
	public:
		string()
			:_str(new char[1])
			, _size(0)
			,_capacity(0)
		{
			//空字符串只有\0,
			_str[0] = '\0';
		}
		//构造非空字符串
		string(const char * str)
			//开辟空间,多开辟个空间存放\0
			:_str(new char[strlen(str) + 1])
		{
			//开辟空间
			_capacity = _size = strlen(str);
			//拷贝数据
			strcpy(_str, str);
		}


		//拷贝构造函数
		//s2(s1),使用s1拷贝构造s2
		string(string & s)
		{
			//开辟空间
			char* temp = new char[s._capacity + 1];
			_str = temp;
			_capacity = s._capacity;
			_size = s._size;
			//拷贝数据
			strcpy(_str, s._str);
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_size = _capacity = 0;
		}
		
	private:
		//定义成员变量并且给缺省值
		char* _str;
		int   _size;
		int   _capacity;
	};

}
1.1.2:写法二(给缺省值)
代码语言:javascript
复制
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;

//避免与库中的string冲突,因此使用命名空间
namespace MyString
{
	class string
	{
	public:
		//string()
		//	:_str(new char[1])
		//	, _size(0)
		//	,_capacity(0)
		//{
		//	//空字符串只有\0,
		//	_str[0] = '\0';
		//}
		////构造非空字符串
		//string(const char * str)
		//	//开辟空间,多开辟个空间存放\0
		//	:_str(new char[strlen(str) + 1])
		//{
		//	//开辟空间
		//	_capacity = _size = strlen(str);
		//	//拷贝数据
		//	strcpy(_str, str);
		//}
		//给缺省值,构造空字符串时使用缺省值
		string(const char* str = "")
			//开辟空间,多开辟个空间存放\0
			:_str(new char[strlen(str) + 1])
		{
			//开辟空间
			_capacity = _size = strlen(str);
			//拷贝数据
			strcpy(_str, str);
		}

		/*拷贝构造函数
		s2(s1),使用s1拷贝构造s2*/
		string(string & s)
		{
			//开辟空间
			char* temp = new char[s._capacity + 1];
			_str = temp;
			_capacity = s._capacity;
			_size = s._size;
			//拷贝数据
			strcpy(_str, s._str);
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_size = _capacity = 0;
		}
		
	private:
		//定义成员变量并且给缺省值
		char* _str = nullptr;
		int   _size = 0;
		int   _capacity = 0;
	};

}

1.2:赋值运算符重载与operatror[]获取元素

代码语言:javascript
复制
		//s1 = s2 this---->s1   s2----->s
		//赋值运算符重载
		string & operator =(string & s)
		{
			//开辟新空间
			char * temp = new char[s._capacity + 1];
			//释放旧空间
			delete _str;
			//拷贝数据
			strcpy(temp, s._str);
			//指向新空间
			_str = temp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}

		//T &,返回引用同样可以提高效率,有返回值的目的是为了支持连续赋值.
		char & operator[](size_t position)
		{
			assert(position < _size);
			return _str[position];
		}
		//const成员函数,给const对象调用,与上面构成函数重载
		const char& operator[](size_t position) const
		{
			assert(position < _size);
			return _str[position];
		}

1.3:容量与迭代器

迭代器呢其实是一个类指针,因此我们在模拟实现的时候,直接通过指针来进行模拟即可~

代码语言:javascript
复制
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;

//避免与库中的string冲突,因此使用命名空间
namespace MyString
{
	class string
	{
		typedef char* iterator;
		typedef const char* const_iterator;
	

		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//设置成员函数
		const_iterator begin() const
		{
			return _str;
		}

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

		//capacity
		size_t size()
		{
			return _size;
		}
	
		size_t capacity()
		{
			return _capacity;
		}
	
		
	private:
		//定义成员变量并且给缺省值
		char* _str = nullptr;
		int   _size = 0;
		int   _capacity = 0;
	};

}

1.4:reserve与resize

我们来回顾下reserve与resize的相关知识

1:reserve

  • 比size小不变化
  • 比size大但小于capacity也不发生变化
  • 比capacity大才会进行扩容

因此我们只要判断是否比capacity大 2:resize

  • 比size小则进行删除.
  • 比size大但小于capacity会改变size,并用\0插入.
  • 比capacity大,会先改变size的大小同时进行扩容.
代码语言:javascript
复制
		//仅需判断是否比capacity大
		void reserve(size_t n)
		{
			if(n > _capacity)
			{
				//开辟新空间
				char* temp = new char[n + 1];
				//拷贝数据
				strcpy(temp, _str);
				//释放旧空间
				delete[] _str;
				//指向新空间
				_str = temp;
				_capacity = n;
				
			}
		}
		void resize(size_t n,char ch = '\0')
		{
			//比size小则会进行删除
			if (n <= _size)
			{
				_str[n] = ch;
			}
			//比size大比capacity小会改变size,并且用\0插入
			else if( n > _size && n <= _capacity)
			{
				_size = n;
			}
			//比capacity大, 会先改变size的大小同时进行扩容.
			else
			{
				//扩容
				reserve(n);
				for(size_t i = _size; i < _capacity ; i++)
				{
					_str[i] = ch;
				}
				_size = n;
			}
		}

1.5:清空与判断是否为空

代码语言:javascript
复制
		//只清除有效字符,不改变底层空间
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

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

这里博主就不带着uu们测试啦,感兴趣的uu可以自己下去测试下~

1.6:push_back与append

代码语言:javascript
复制
		//Modifiers
		void push_back(char ch)
		{
			//检查容量
			if (_size == _capacity)
				//二倍扩容
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}

		void append(const char * str)
		{
			assert(str);
			//求出添加的字符串的长度
			int length = strlen(str);
			//判断添加后的有效字符数量是否越过容量
			if (_size + length > _capacity)
				reserve(_size + length);
			//_str + _size的位置为\0
			strcpy(_str + _size, str);
            _size += length;
		}

1.7:insert

1.7.1:插入字符
代码语言:javascript
复制
		string & insert(size_t position,char ch)
		{
			assert(position <= _size);
			if (_size == _capacity)
				//二倍扩容
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			//从后向前挪动覆盖
			for(int i = _size - 1; i >= position ;i--)
			{
				_str[i + 1] = _str[i];
			}
			_str[position] = ch;
			_size++;
			_str[_size] = '\0';
			return *this;
		}
1.7.2:插入字符串
代码语言:javascript
复制
		string & insert(size_t position,const char * str)
		{
			assert(position <= _size);
			int length = strlen(str);
			if (_size + length > _capacity)
				reserve(_size + length);
			//从后向前挪动覆盖
			//_size + length - 1表示挪动的字符串的重点,positon + length表示挪动的字符串的起点
			for(int i = _size + length - 1; i >= position + length; i--)
			{
				_str[i] = _str[i - length];
			}
			//拷贝数据
			strncpy(_str + position, str, length);
			_size += length;
			_str[_size] = '\0';
			return *this;
		}

1.8:erase

代码语言:javascript
复制
		void erase(size_t position = 0,size_t len = -1)
		{
			//防止越界
			assert(position < _size);
			//len == -1:如果 len 为 -1,表示删除到字符串末尾。
			//len >= _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是删除到末尾。
			if(len == -1 || len >= _size - position)
			{
				_str[position] = '\0';
				_size = position;
			}

			else
			{
				//将 pos + len 后的字符串内容复制到 pos 位置,从而覆盖中间 len 长度的字符,实现删除操作
				strcpy(_str + position, _str + position + len);
			}
		}

1.9:operator+=

1.9.1:添加字符
代码语言:javascript
复制
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
1.9.2:添加字符串
代码语言:javascript
复制
		string& operator+=(const char * str)
		{
			append(str);
			return *this;
		}

1.91:find

1.91.1:查找字符
代码语言:javascript
复制
		//添加const修饰,令const成员与非const成员均可调用
		size_t find(char ch, size_t pos = 0) const
		{
			for(size_t i = pos; i < _size;i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return -1;
		}
1.91.2:查找字符串
代码语言:javascript
复制
		const char * c_str()const
		{
			return _str;
		}

		//添加const修饰,令const成员与非const成员均可调用
		size_t find(const char * str,size_t pos = 0	)const
		{
			const char* p = strstr(_str + pos, str);
			if(p != NULL)
			{
				//指针 - 指针得到对应的首字符下标
				return p - _str;
			}
			return -1;
		}

1.92:substr与swap

代码语言:javascript
复制
		string substr(size_t pos = 0, size_t len = -1)
		{
			string substr;
			//len == -1:如果 len 为 -1,表示全部获取。
			//len > _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是全部获取
			if(len > _size - pos || len == -1)
			{
				for(size_t i = 0; i < _size; i++)
				{
					substr += _str[i];
				}
			}
			else
			{
				for(size_t i = pos; i < pos + len;i++)
				{
					substr += _str[i];
				}
			}
			return substr;
		}
		//s1.swap(s2)
		void swap(string & str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

1.93:非成员函数的重载

1.93.1:流插入与流提取的重载
代码语言:javascript
复制
	ostream& operator<<(ostream& _cout,MyString::string& str)
	{
		for (auto ch : str)
		{
			cout << ch;
		}
		return _cout;
	}

    istream& operator>>(istream& _cin, MyString::string& str)
	{
		////第一版:会导致空间的浪费
		//char ch;
		////_cin是无法读到\n与' '的
		//ch = _cin.get();
		////清空字符
		//str.clear();
		//while (ch != '\n' && ch != ' ')
		//{
		//	str += ch;
		//	ch = _cin.get();
		//}
		//return _cin;

		//第二版
		char ch;
		ch = _cin.get();
		//底层给个buff
		char buff[128];
		size_t i = 0;
		//将原本的字符清空
		str.clear();
		//cin无法读取' '与'\n'
		while (ch != '\n' && ch != ' ')
		{
			buff[i++] = ch;
			//最后一个字符设置为\0
			if(i == 127)
			{
				buff[i] = '\0';
				str += buff;
			}
			//持续读取字符
			ch = _cin.get();
		}
		if(i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return _cin;
	} 
}

按照常规方式,流提取按照上面的第一版方式进行重载即可,但是,底层其实是给了buff,目的是: 防止空间的浪费,因为按照常规方式重载的话,那么在 扩容的时候一般是1.5倍或者2倍扩容,而通过给一个buff,能够最大程度地防止空间的浪费.

1.93.2:其他非成员函数的重载
代码语言:javascript
复制
	//会先调用此swap,有现成的,吃现成的,不使用模版里面的swap
	void swap(string& x, string& y)
	{
		x.swap(y);
	}

	//自定义类型传值传参会调用拷贝构造,因此需要传引用
	bool operator==(const MyString::string& str1, const  MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result == 0;
	}

	bool operator<(const  MyString::string& str1, const MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result < 0;
	}

	bool operator>(const  MyString::string& str1, const  MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result > 0;
	}

	bool operator<=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return (str1 < str2) || (str1 == str2);
	}

	bool operator>=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return !(str1 < str2);
	}

	bool operator!=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return !(str1 == str2);
	}

2:Test.cpp

2.1:构造函数与拷贝构造

2.1.1:测试写法一
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestConstructionAndCopyConstruction()
{
	MyString::string s1;
	MyString::string s2("hello world");
	MyString::string s3(s2);
}
int main()
{
	TestConstructionAndCopyConstruction();
	return 0;
}
2.1.2:测试写法二
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestConstructionAndCopyConstruction()
{
	MyString::string s1;
	MyString::string s2("hello world");
	MyString::string s3(s2);
}
int main()
{
	TestConstructionAndCopyConstruction();
	return 0;
}

2.2:测试赋值运算符重载与[]获取元素

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void TestAssignmentOperatorOverloadingAndElementAccess()
{
	MyString::string s1;
	MyString::string s2("hello world");
	const MyString::string s3("hello Linux");
	s1 = s2;
	cout <<"s1[0]:>" << s1[0] << endl;
	cout <<"s3[6]:>" << s3[6] << endl;
}
int main()
{
	TestAssignmentOperatorOverloadingAndElementAccess();
	return 0;
}

2.3:测试迭代器与容量

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void TestIteratorAndCapacity()
{
	MyString::string s1;
	MyString::string s2("hello world");
	s1 = s2;
	for(auto & element : s2)
	{
		cout << element << endl;
	}
	cout <<"s2.size()" << s2.size() << endl;
	cout <<"s2.capacity()" << s2.capacity() << endl;
}

int main()
{
	TestIteratorAndCapacity();
	return 0;
}

2.4:测试reserve与resize

2.4.1:测试resize
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestResize()
{
	MyString::string str("hello world");
	//比size小则进行删除
	str.resize(10);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;
	//比size大但小于capacity会用改变size,并用\0插入
	str.resize(13);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
	cout << endl;

	//比capacity大,会先改变size的大小同时进行扩容
	str.resize(20);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
}

int main()
{
	TestResize();
	return 0;
}
2.4.2:测试reserve
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestRserve()
{
	MyString::string str("hello world");
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
	cout << "reserver后" << endl;

	cout << endl;

	//比size小不变化
	str.reserve(10);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;
	//比size大但小于capacity也不发生变化
	str.reserve(13);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;

	//比capacity大才会进行扩容
	str.reserve(20);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
}

int main()
{
	TestRserve();
	return 0;
}

2.5:测试push_back与append

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void TestPushBackAndAppend()
{
	MyString::string str;
	str.append("hello world");
	str.push_back('x');
}

int main()
{
	TestPushBackAndAppend();
	return 0;
}

2.6:测试insert

2.6.1:测试插入字符
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestInsert() 
{
	MyString::string str("hello world");
	//在下标为5的位置插入字符h
	str.insert(5, 'h');
}

int main()
{
	TestInsert();
	return 0;
}

2.6.2:测试插入字符串
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestInsert() 
{
	MyString::string str("hello world");
	//在下标为5的位置插入字符h
	str.insert(5, 'h');
	str.insert(2, "Linux");
}

int main()
{
	TestInsert();
	return 0;
}

2.7:测试erase

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void TestErase()
{
	MyString::string str("hello world");
	str.erase(2, 4);
}

int main()
{
	TestErase();
	return 0;
}

2.8:测试operator+=

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void Test()
{
	MyString::string str("hello");
	str += ' ';
	str += "Linux";
}

int main()
{
	Test();
	return 0;
}

2.9:测试find

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"


void TestFind() 
{
	MyString::string str("hello Linux");
	cout << str.find('l', 2) << endl;
	cout << str.find("Linux", 2) << endl;
}

int main()
{
	TestFind();
	return 0;
}

2.91:测试substr与swap

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestSwapAndSubstr()
{
	MyString::string s1("hello Linux");
	MyString::string s2("hello bit");
	MyString::string temp = s1.substr(6, 5);
	cout << "交换前" << endl;
	cout << "s1:>" << s1 << endl;
	cout << "s2:>" << s2 << endl;
	cout <<"temp:>" << temp << endl;
	s1.swap(s2);
	cout << "交换后" << endl;
	cout <<"s1:>" << s1 << endl;
	cout <<"s2:>" << s2 << endl;
}
int main()
{
	TestSwapAndSubstr();
	return 0;
}

2.92:测试流插入与流提取

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestStreamInsertionAndStreamExtraction()
{
	MyString::string s1;
	cin >> s1;
	cout << s1 << endl;
	cout << "s1.size:>" << s1.size() << endl;
	cout << "s1.capacity:>" << s1.capacity() << endl;
}
int main()
{
	TestStreamInsertionAndStreamExtraction();
	return 0;
}
2.92.1:第一版流提取
2.92.2:第二版流提取

2.93:测试其他非成员函数

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"
void TestOtherFunction()
{
	MyString::string s1("hello world");
	MyString::string s2("hello bit");
	swap(s1, s2);
	s1.swap(s2);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;

	cout << (s1 == s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 > s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 != s2) << endl;
}

int main()
{
	TestOtherFunction();
	return 0;
}

3:知识补充

3.1:深拷贝与浅拷贝

在讲深浅拷贝之前,我们来看一个现象

我们可以清晰地看到,s2与s3的地址一样,这是为什么呢,因为博主将显示定义的拷贝构造函数给屏蔽了,因此在拷贝构造s3时会调用编译器默认的拷贝构造函数,那么这就会导致一个问题: s2与s3共用同一块内存空间,在释放时同一块内存空间被释放多次而会引起程序崩溃,这种方式被称作浅拷贝.

3.1.1:浅拷贝

浅拷贝:又被称作位拷贝,编译器直接是将另外一个对象的值拷贝复制过来.如果对象中管理资源,那么最后就会导致多个对象共享一份资源,当一个对象销毁时就会将资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还是有效的,那么因此当继续对资源进行操作时,就会发生访问违规~ 举一个简单例子 就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我抢,玩具损坏.

那么该如何解决浅拷贝的问题呢,用深拷贝就可以即每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有存在任何矛盾.

3.1.2:深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出,一般情况都是按照深拷贝方式提供.

4:总代码

4.1:string.h

代码语言:javascript
复制
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;

//避免与库中的string冲突,因此使用命名空间
namespace MyString
{
	class string
	{
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		//string()
		//	:_str(new char[1])
		//	, _size(0)
		//	,_capacity(0)
		//{
		//	//空字符串只有\0,
		//	_str[0] = '\0';
		//}
		////构造非空字符串
		//string(const char * str)
		//	//开辟空间,多开辟个空间存放\0
		//	:_str(new char[strlen(str) + 1])
		//{
		//	//开辟空间
		//	_capacity = _size = strlen(str);
		//	//拷贝数据
		//	strcpy(_str, str);
		//}


		//给缺省值,构造空字符串时使用缺省值
		string(const char* str = "")
			//开辟空间,多开辟个空间存放\0
			:_str(new char[strlen(str) + 1])
		{
			//开辟空间
			_capacity = _size = strlen(str);
			//拷贝数据
			strcpy(_str, str);
		}

		/*拷贝构造函数
		s2(s1),使用s1拷贝构造s2*/
		string(string& s)
		{
			//开辟空间
			char* temp = new char[s._capacity + 1];
			_str = temp;
			_capacity = s._capacity;
			_size = s._size;
			//拷贝数据
			strcpy(_str, s._str);

		}

		//s1 = s2 this---->s1   s2----->s
		//赋值运算符重载
		string& operator =(string& s)
		{
			//开辟新空间
			char* temp = new char[s._capacity + 1];
			//释放旧空间
			delete _str;
			//拷贝数据
			strcpy(temp, s._str);
			//指向新空间
			_str = temp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}

		//T &,返回引用同样可以提高效率,有返回值的目的是为了支持连续赋值.
		char& operator[](size_t position)
		{
			assert(position < _size);
			return _str[position];
		}
		//const成员函数,给const对象调用,与上面构成函数重载
		const char& operator[](size_t position) const
		{
			assert(position < _size);
			return _str[position];
		}
		///////////////////////////////////////////////////////////
		//iteraor
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//设置成员函数
		const_iterator begin() const
		{
			return _str;
		}

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

		//capacity
		size_t size()
		{
			return _size;
		}

		size_t capacity()
		{
			return _capacity;
		}

		//仅需判断是否比capacity大
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				//开辟新空间
				char* temp = new char[n + 1];
				//拷贝数据
				strcpy(temp, _str);
				//释放旧空间
				delete[] _str;
				//指向新空间
				_str = temp;
				_capacity = n;

			}
		}
		void resize(size_t n, char ch = '\0')
		{
			//比size小则会进行删除
			if (n <= _size)
			{
				_str[n] = ch;
			}
			//比size大比capacity小会改变size,并且用\0插入
			else if (n > _size && n <= _capacity)
			{
				_size = n;
			}
			//比capacity大, 会先改变size的大小同时进行扩容.
			else
			{
				//扩容
				reserve(n);
				for (size_t i = _size; i < _capacity; i++)
				{
					_str[i] = ch;
				}
				_size = n;
			}
		}

		//Modifiers
		void push_back(char ch)
		{
			//检查容量
			if (_size == _capacity)
				//二倍扩容
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			assert(str);
			//求出添加的字符串的长度
			int length = strlen(str);
			//判断添加后的有效字符数量是否越过容量
			if (_size + length > _capacity)
				reserve(_size + length);
			//_str + _size的位置为\0
			strcpy(_str + _size, str);
			_size += length;
		}

		string& insert(size_t position, char ch)
		{
			assert(position <= _size);
			if (_size == _capacity)
				//二倍扩容
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			//从后向前挪动覆盖
			for (int i = _size - 1; i >= position; i--)
			{
				_str[i + 1] = _str[i];
			}
			_str[position] = ch;
			_size++;
			_str[_size] = '\0';
			return *this;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

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

		string& insert(size_t position, const char* str)
		{
			assert(position <= _size);
			int length = strlen(str);
			if (_size + length > _capacity)
				reserve(_size + length);
			//从后向前挪动覆盖
			//_size + length - 1表示挪动的字符串的重点,positon + length表示挪动的字符串的起点
			for (int i = _size + length - 1; i >= position + length; i--)
			{
				_str[i] = _str[i - length];
			}
			//拷贝数据
			strncpy(_str + position, str, length);
			_size += length;
			_str[_size] = '\0';
			return *this;
		}

		void erase(size_t position = 0, size_t len = -1)
		{
			//防止越界
			assert(position < _size);
			//len == -1:如果 len 为 -1,表示删除到字符串末尾。
			//len >= _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是删除到末尾。
			if (len == -1 || len >= _size - position)
			{
				_str[position] = '\0';
				_size = position;
			}

			else
			{
				//将 pos + len 后的字符串内容复制到 pos 位置,从而覆盖中间 len 长度的字符,实现删除操作
				strcpy(_str + position, _str + position + len);
			}
		}

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

		const char * c_str()const
		{
			return _str;
		}

		//添加const修饰,令const成员与非const成员均可调用
		size_t find(char ch, size_t pos = 0) const
		{
			for(size_t i = pos; i < _size;i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return -1;
		}

		//添加const修饰,令const成员与非const成员均可调用
		size_t find(const char * str,size_t pos = 0	)const
		{
			const char* p = strstr(_str + pos, str);
			if(p != NULL)
			{
				//指针 - 指针得到对应的首字符下标
				return p - _str;
			}
			return -1;
		}

		string substr(size_t pos = 0, size_t len = -1)
		{
			string substr;
			//len == -1:如果 len 为 -1,表示全部获取。
			//len > _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是全部获取
			if(len > _size - pos || len == -1)
			{
				for(size_t i = 0; i < _size; i++)
				{
					substr += _str[i];
				}
			}
			else
			{
				for(size_t i = pos; i < pos + len;i++)
				{
					substr += _str[i];
				}
			}
			return substr;
		}
		//s1.swap(s2)
		void swap(string & str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_size = _capacity = 0;
		}
		
	private:
		//定义成员变量并且给缺省值
		char* _str = nullptr;
		int   _size = 0;
		int   _capacity = 0;
	};
	//Non-member_function_overloads
	ostream& operator<<(ostream& _cout,MyString::string& str)
	{
		for (auto ch : str)
		{
			cout << ch;
		}
		return _cout;
	}

	istream& operator>>(istream& _cin, MyString::string& str)
	{
		////第一版:会导致空间的浪费
		//char ch;
		////_cin是无法读到\n与' '的
		//ch = _cin.get();
		////清空字符
		//str.clear();
		//while (ch != '\n' && ch != ' ')
		//{
		//	str += ch;
		//	ch = _cin.get();
		//}
		//return _cin;

		//第二版
		char ch;
		ch = _cin.get();
		//底层给个buff
		char buff[128];
		size_t i = 0;
		//将原本的字符情况
		str.clear();
		//cin无法读取' '与'\n'
		while (ch != '\n' && ch != ' ')
		{
			buff[i++] = ch;
			//最后一个字符设置为\0
			if(i == 127)
			{
				buff[i] = '\0';
				str += buff;
			}
			//持续读取字符
			ch = _cin.get();
		}
		if(i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return _cin;
	}

	//会先调用此swap,有现成的,吃现成的,不使用模版里面的swap
	void swap(string& x, string& y)
	{
		x.swap(y);
	}

	//自定义类型传值传参会调用拷贝构造,因此需要传引用
	bool operator==(const MyString::string& str1, const  MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result == 0;
	}

	bool operator<(const  MyString::string& str1, const MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result < 0;
	}

	bool operator>(const  MyString::string& str1, const  MyString::string& str2)
	{
		int result = strcmp(str1.c_str(), str2.c_str());
		return result > 0;
	}

	bool operator<=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return (str1 < str2) || (str1 == str2);
	}

	bool operator>=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return !(str1 < str2);
	}

	bool operator!=(const  MyString::string& str1, const  MyString::string& str2)
	{
		return !(str1 == str2);
	}
}

4.2:Test.cpp

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"

void TestConstructionAndCopyConstruction()
{
	MyString::string s1;
	MyString::string s2("hello world");
	MyString::string s3(s2);
}

void TestAssignmentOperatorOverloadingAndElementAccess()
{
	MyString::string s1;
	MyString::string s2("hello world");
	const MyString::string s3("hello Linux");
	s1 = s2;
	cout <<"s1[0]:>" << s1[0] << endl;
	cout <<"s3[6]:>" << s3[6] << endl;
}

void TestIteratorAndCapacity()
{
	MyString::string s1;
	MyString::string s2("hello world");
	s1 = s2;
	for(auto & element : s2)
	{
		cout << element;
	}
	cout << endl;
	cout <<"s2.size():>" << s2.size() << endl;
	cout <<"s2.capacity():>" << s2.capacity() << endl;
}


void TestResize()
{
	MyString::string str("hello world");
	//比size小则进行删除
	str.resize(10);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;
	//比size大但小于capacity会用改变size,并用\0插入
	str.resize(13);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
	cout << endl;

	//比capacity大,会先改变size的大小同时进行扩容
	str.resize(20);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
}

void TestRserve()
{
	MyString::string str("hello world");
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
	cout << "reserver后" << endl;

	cout << endl;

	//比size小不变化
	str.reserve(10);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;
	//比size大但小于capacity也不发生变化
	str.reserve(13);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;

	cout << endl;

	//比capacity大才会进行扩容
	str.reserve(20);
	cout << "size:>" << str.size() << endl;
	cout << "capacity:>" << str.capacity() << endl;
}

void TestPushBackAndAppend()
{
	MyString::string str;
	str.append("hello world");
	str.push_back('x');
}
void TestInsert() 
{
	MyString::string str("hello world");
	//在下标为5的位置插入字符h
	str.insert(5, 'h');
	str.insert(2, "Linux");
}

void TestErase()
{
	MyString::string str("hello world");
	str.erase(2, 4);
}

void Test()
{
	MyString::string str("hello");
	str += ' ';
	str += "Linux";
}

void TestFind() 
{
	MyString::string str("hello Linux");
	cout << str.find('l', 2) << endl;
	cout << str.find("Linux", 2) << endl;
}
void TestSwapAndSubstr()
{
	MyString::string s1("hello Linux");
	MyString::string s2("hello bit");
	MyString::string temp = s1.substr(6, 5);
	cout << "交换前" << endl;
	cout << "s1:>" << s1 << endl;
	cout << "s2:>" << s2 << endl;
	cout <<"temp:>" << temp << endl;
	s1.swap(s2);
	cout << "交换后" << endl;
	cout <<"s1:>" << s1 << endl;
	cout <<"s2:>" << s2 << endl;
}

void TestStreamInsertionAndStreamExtraction()
{
	MyString::string s1;
	cin >> s1;
	cout << s1 << endl;
	cout << "s1.size:>" << s1.size() << endl;
	cout << "s1.capacity:>" << s1.capacity() << endl;
}

void TestOtherFunction()
{
	MyString::string s1("hello world");
	MyString::string s2("hello bit");
	swap(s1, s2);
	s1.swap(s2);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;

	cout << (s1 == s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 > s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 != s2) << endl;
}

int main()
{
	TestConstructionAndCopyConstruction();
	TestAssignmentOperatorOverloadingAndElementAccess();
	TestIteratorAndCapacity();
	TestResize();
	TestRserve();
	TestPushBackAndAppend();
	TestInsert();
	TestErase();
	Test();
	TestFind();
	TestSwapAndSubstr();
	TestStreamInsertionAndStreamExtraction();
	TestOtherFunction();
	return 0;
}

好啦,uu们,string的模拟实现这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1:string.h
    • 1.1:构造函数与拷贝构造函数
      • 1.1.1:写法一
      • 1.1.2:写法二(给缺省值)
    • 1.2:赋值运算符重载与operatror[]获取元素
    • 1.3:容量与迭代器
    • 1.4:reserve与resize
    • 1.5:清空与判断是否为空
    • 1.6:push_back与append
    • 1.7:insert
      • 1.7.1:插入字符
      • 1.7.2:插入字符串
    • 1.8:erase
    • 1.9:operator+=
      • 1.9.1:添加字符
      • 1.9.2:添加字符串
    • 1.91:find
      • 1.91.1:查找字符
      • 1.91.2:查找字符串
    • 1.92:substr与swap
    • 1.93:非成员函数的重载
      • 1.93.1:流插入与流提取的重载
      • 1.93.2:其他非成员函数的重载
  • 2:Test.cpp
    • 2.1:构造函数与拷贝构造
      • 2.1.1:测试写法一
      • 2.1.2:测试写法二
    • 2.2:测试赋值运算符重载与[]获取元素
    • 2.3:测试迭代器与容量
    • 2.4:测试reserve与resize
      • 2.4.1:测试resize
      • 2.4.2:测试reserve
    • 2.5:测试push_back与append
    • 2.6:测试insert
      • 2.6.1:测试插入字符
      • 2.6.2:测试插入字符串
    • 2.7:测试erase
    • 2.8:测试operator+=
    • 2.9:测试find
    • 2.91:测试substr与swap
    • 2.92:测试流插入与流提取
      • 2.92.1:第一版流提取
      • 2.92.2:第二版流提取
    • 2.93:测试其他非成员函数
  • 3:知识补充
    • 3.1:深拷贝与浅拷贝
      • 3.1.1:浅拷贝
  • 4:总代码
    • 4.1:string.h
    • 4.2:Test.cpp
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档