【C++】:STL中的string类的增删查改的底层模拟实现

2024-02-26 1018阅读

温馨提示:这篇文章已超过389天没有更新,请注意相关的内容是否还可用!

【C++】:STL中的string类的增删查改的底层模拟实现

本篇博客仅仅实现存储字符(串)的string

同时由于C++string库设计的不合理,我仅实现一些最常见的增删查改接口

接下来给出的接口都是基于以下框架:

private:
		char* _str;//思考如何不用const
		size_t _size;
		size_t _capacity;
		//这样写可以
		const static size_t string::npos = -1;
		//下面这种写法也可以->和上述写法产生矛盾
		const static size_t string::npos;
	};
	//静态变量只能在类外面定义 上述两种定义容易产生矛盾

📚1.string默认构造 | 析构函数 | 拷贝构造 | 赋值重载

//无参默认构造
string()
	:_str(new char[1])
	, _size(0)
	, _capacity(0)
{
	_str[0] = '\0';
}
//有参默认构造
string(const char* str="\0")//不能写'\0'
	//->类型不匹配
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

C++string标准库中,无参构造并不是空间为0,直接置为空指针 而是开一个字节,并存放‘\0’

C++中支持无参构造一个对象后,直接在后面插入数据,也从侧面说明了这点

由于C++构造函数不管写不写都会走初始化列表的特性,所以这里也走初始化列表

string中,_capacity和_size都不包含空指针,所以带参构造要多开一个空间,用来存储’\0’

📚2.string类析构函数

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

📚3.拷贝构造

string(const string& s)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

📚4.赋值重载

传统写法:

string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

现代写法:

//法二
void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
string& operator=(string tmp)
{
	swap(tmp);
	return *this;
}

📚5.iterator迭代器和基于范围的for循环

在C++中,范围for在底层是通过迭代器来实现的 所以只要实现了迭代器,就支持范围for

而迭代器类似于指针,迭代器可以被看作是指针的一种泛化,它提供了类似指针的功能,可以进行解引用操作、指针运算等

以下提供了const迭代器和非const迭代器:

typedef char* iterator;
const typedef 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;
}

📚6.运算符重载operator[ ]

这里我们和库中一样,提供以下两个版本

//可读可写
char operator[](size_t pos)
{
	assert(pos  
char& operator[](size_t pos) const
{
	assert(pos  
bool operator>(const string& s)
{
	return strcpy(_str, s._str) == 0;
}
bool operator==(const string& s)
{
	return strcpy(_str, s._str) == 0;
}
bool operator>=(const string& s)
{
	return *this > s || *this == s;
}
bool operator
	return !(*this = s);
	//return strcpy(_str, s._str) == 0;
}
bool operator!=(const string& s)
{
	return !(*this == s);
}

这里我用了this指针指向的是private里面的数据 然后和s对象所对应的值作比较 能和strcpy函数联合起来用 能达到同样的效果 这是一个很不错的想法

📚7.容量相关:size | resize | capacity | reserve

size_t size()const
{
	return _size;
}
size_t capacity()const
{
	return _capacity;
}

在C++中,我们一般不缩容

所以实现reserve时容量调整到n首先判断目标容量n是否大于当前容量。如果小于就不做处理,否则先开辟n+1个内存空间(多出来的一个用于存储‘\0’),然后将原有数据拷贝到新空间strcpy会将’\0’一并拷贝过去然后释放就空间,并让_str指向新空间,同时更新_capacity

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

当n if (n _str[n] = '\0'; _size = n; } else { reserve(n); while (_size =0, 会导致死循环

解决办法如下

从end从最后数据的后一位开始,每次将前一个数据移到当前位置。最后条件判断就转化为end>pos,不会出现死循环这种情况

void insert(size_t pos, char ch)
{
	assert(pos 
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//挪动数据
	size_t end = _size+1;
	while (end  pos)
	{
		_str[end] = _str[end-1];
		end--;
	}
	//插入数据,更新_size
	_str[pos] = ch;
	_size++;
}

insert插入字符串

void insert(size_t pos, const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	
	size_t end = _size+1;
	while (end > pos)
	{
		_str[end + len-1] = _str[end-1];
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

erase

erase分两种情况:

从pos开始,要删的数据个数超过的字符串,即将pos后序所有数据全部情况 直接将pos处数据置为’\0’即可

从pos开始,要删的数据个数没有超出的字符串。所以只需要从pos+len位置后的所有数据向前移动从pos位置覆盖原数据即可

void erase(size_t pos, size_t len = npos)
{
	//len==nppos意思是字串全部删除
	if (len==npos || pos + len >= _size)
	{
		//有多少,删多少
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin 
			_str[begin - len] = _str[begin];
			begin++;
		}
		_size -= len;
	}
}
h2📚9.关系操作符重载:= | !=
bool operator>(const string& s)
{
	return strcpy(_str, s._str) == 0;
}
bool operator==(const string& s)
{
	return strcpy(_str, s._str) == 0;
}
bool operator>=(const string& s)
{
	return *this > s || *this == s;
}
bool operator
	return !(*this = s);
	//return strcpy(_str, s._str) == 0;
}
bool operator!=(const string& s)
{
	return !(*this == s);
}

📚10.find查找字符 | 字符串 | substr

size_t find(char ch, size_t pos = 0)
{
	for (size_t i = pos; i  
size_t find(const char* sub, size_t pos = 0)
{
	const char* p = strstr(_str + pos, sub);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}

strsub()模拟实现

strsub目标长度可能越界string,也可能还有没有。但不管是那种情况,最后都需要拷贝数据。所以这里我们可以先将len真实长度计算出来,在拷贝数据

string substr(size_t pos, size_t len = npos)const
{
	string s;
	size_t end = pos + len;
	//目标字符越界string,更新len
	if (len == npos || end >= _size)
	{
		len = _size - pos;
		end = _size;
	}
	//拷贝数据
	s.reserve(len);
	for (size_t i = pos; i  

📚11.流插入和流提取>

由于前面我们实现了迭代器,所以最简单的方式就是范围for

ostream& operator
	/*for (size_t i = 0; i 

所以这里博主直接调用istream对象中的get()函数类似于C语言中的getchar()函数

class string
{
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
private:
	char* _str;
	size_t _capacity;
	size_t _size;
};
istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			//in >> ch;
			ch = in.get();
		}
		return in;
	}

📚12.完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#include
using namespace std;
namespace success
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		string()
			:_str(new char[1])
			, _size(0)
			, _capacity(0)
		{
			_str[0] = '\0';
		}
		string(const char* str="\0")//不能写'\0'
			//->类型不匹配
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		const char* c_str()
		{
			return _str;
		}
		string(const string& str)
			:_size(str._size)
		{
			_capacity = str._capacity;
			_str = new char[str._capacity + 1];
			strcpy(_str, str._str);
		}
		char& operator[](size_t pos) const
		{
			assert(pos (const string& s)
		{
			return strcpy(_str, s._str) == 0;
		}
		bool operator==(const string& s)
		{
			return strcpy(_str, s._str) == 0;
		}
		bool operator>=(const string& s)
		{
			return *this > s || *this == s;
		}
		bool operator
			return !(*this = s);
			//return strcpy(_str, s._str) == 0;
		}
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
		void resize(size_t n, char ch = '\0');
		void reserve(size_t n)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
		void push_back(char ch)
		{
			if (_size + 1 > _capacity)
			{			
				reserve(_capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			//strcat(_str, str);
			_size += len;
		}
		void insert(size_t pos, char ch)
		{
			assert(pos  _capacity)
			{
				reserve(2 * _capacity);
			}
			size_t end = _size;
			while (end >= pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}
			_str[pos] = ch;
			++_size;
		}
		void insert(size_t pos, const char* str)
		{
			int len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end + len-1] = _str[end-1];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}
		void erase(size_t pos, size_t len = npos)
		{
			//len==nppos意思是字串全部删除
			if (len==npos || pos + len >= _size)
			{
				//有多少,删多少
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin 
					_str[begin - len] = _str[begin];
					begin++;
				}
				_size -= len;
			}
		}
	private:
		char* _str;//思考如何不用const
		size_t _size;
		size_t _capacity;
		//这样写可以
		const static size_t string::npos = -1;
		//下面这种写法也可以-和上述写法产生矛盾
		const static size_t string::npos;
	};
	//静态变量只能在类外面定义 上述两种定义容易产生矛盾
	void test_string1()
	{
		string s1("hello bit");
		string s2("hello bit.");
		cout 
		string s3 = ("hello bit");
		string s4("hello bit.");
		cout 
		string s1 = "hello bit";
		string::iterator it = s1.begin();
		while (it != s1.end())
		{
			cout 
	try
	{
		success::test_string2();
	}
	catch (const exception& e)
	{
		cout 
	//	cout 
	//	cout 
	//	cout 
	//	strResult = strText.substr(size_prev_pos, size_pos - size_prev_pos);
	//	cout 
	//	strResult = strText.substr(size_prev_pos, size_pos - size_prev_pos);
	//	cout 
public:
/*String()
:_str(new char[1])
{*_str = '\0';}
*/
//String(const char* str = "\0") 错误示范
//String(const char* str = nullptr) 错误示范
String(const char* str = "")
{
// 构造String类对象时,如果传递nullptr指针,可以认为程序非
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
// 测试
void TestString()
{
String s1("hello bit!!!");
String s2(s1);
}

public:
String(const char* str = "")
{
// 构造String类对象时,如果传递nullptr指针,可以认为程序非
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
if (this != &s)
{
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
}
return *this;
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};

public:
String(const char* str = "")
{
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(nullptr)
{
String strTmp(s._str);
swap(_str, strTmp._str);
}
// 对比下和上面的赋值那个实现比较好?
String& operator=(String s)
{
swap(_str, s._str);
return *this;
}
/*
String& operator=(const String& s)
{
if(this != &s)
{
String strTmp(s);
swap(_str, strTmp._str);
}
return *this;
}
*/
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]