string常用接口的模拟实现

目录

前言

string常用接口的模拟实现

1.成员函数的实现

2.与容量操作有关的接口的实现

3.string对象的修改接口的实现

4.string的一些其他接口

string的模拟实现代码汇总如下:


前言

上一篇文章我们介绍了string类中常用接口的使用方法,但是string底层是如何实现这些接口函数的呢?下面我们将开始探讨string类的模拟实现,希望通过本篇文章能够加深您对string类的理解。

string常用接口的用法如下:

STL——string类常用接口说明

string常用接口的模拟实现

string类是支持声明和定义分离的,所以我们先创建一个string.h文件,将我们要实现的接口在里面声明以下,如下所示:

class string
{
public:
	//迭代器
	typedef char* iterator;
	iterator begin();
	iterator end();

	//const迭代器
	typedef const char* const_iterator;
	const_iterator begin()const;
	const_iterator end()const;

	string(const char* str = "");
	string(const string& str);
	string& operator=(const string& str);
	~string();

	//字符串有效长度
	size_t size()const;
	char& operator[](size_t pos);
	const char& operator[](size_t pos)const;

	//获取字符串
	char* c_str()const;
	//预留n个大小的空间
	void reserve(size_t n);
	//尾插一个字符
	void push_back(char ch);
	//尾插一个字符串
	void append(const char* str);

	//字符串追加
	string& operator+=(char ch);
	string& operator+=(const char*str);

	//字符串插入
	void insert(size_t pos, char ch);
	void insert(size_t pos, const char* str);
	//字符串的删除
	void erase(size_t pos = 0, size_t len = npos);

	//字符串查找
	size_t find(char ch, size_t pos = 0);
	size_t find(const char*str, size_t pos = 0);

	//交换
	void swap(string& s);
	//清除
	void clear();
	//截取一段字符串
	string substr(size_t pos, size_t len);

	//字符串比较
	bool operator>(const string& str)const;
	bool operator>=(const string& str)const;
	bool operator<(const string& str)const;
	bool operator<=(const string& str)const;
	bool operator==(const string& str)const;
	bool operator!=(const string& str)const;
private:
	char* _str;
	size_t _size;
	size_t _capacity;
	const static size_t npos;
};

我们在.h文件中定义好了之后,创建一个string.cpp文件,我们将在这个文件中实现上述定义的接口,需要注意的是:在.cpp文件中要指定类域。 

1.成员函数的实现

首先是string的构造函数,string支持无参的构造和带参构造,为了方便,我们可以将带参构造和无参构造写成同一个函数,我们可以传一个缺省值,代码如下所示:

string::string(const char* str)
	:_size(strlen(str))
{
	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

分析上述代码,我们为_str开辟了一个新的空间,再将str的值拷贝放进_str中,其中我们多开了一个空间,这是为什么呢?实际上多开的一个空间是为\0预留的空间,因为字符串后面隐藏了一个\0作为字符串结束的标志。

回到构造函数的定义,我们发现,我们给构造函数传了一个缺省值“”,这是为什么呢?为了支持无参构造,我们默认给字符串传了一个\0,这样就支持了无参构造。

接下来是拷贝构造函数,代码如下所示:

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

分析上述代码,我们发现与构造函数类似,都是通过新开一段空间,再将要构造的内容拷贝进新的空间里。

析构函数如下所示:

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

在介绍完构造 拷贝构造以及析构函数之后,需要重点介绍的就是赋值重载,其代码如下所示:

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

		_str = tmp;
		_size = str._size;
		_capacity = str._capacity;
	}
	return *this;
}

赋值重载不同于上述,它是通过开辟一段新的空间,将内容拷贝到新的空间,释放旧空间,新空间指向旧空间,从而实现赋值。

值得注意的是,赋值重载是有返回值的,为了提高效率以及连续赋值我们通过引用接收。

2.与容量操作有关的接口的实现

首先就是size函数的实现,由于size函数是获取字符串的有效长度,所以它也是有返回值的,返回值用size_t接收,其代码如下所示:

size_t string::size()const
{
	return _size;
}

接下来就是下标访问操作符的重载,下标访问操作符的访问需要支持普通成员和const成员,代码如下所示:

char& string::operator[](size_t pos)
{
	assert(pos < _size);

	return _str[pos];
}
const char& string::operator[](size_t pos)const
{
	assert(pos < _size);

	return _str[pos];
}

接下来是字符串的访问,代码如下所示:

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

在介绍完上述接口之后,我们如果要访问字符串,则需要配合迭代器使用,下面是迭代器的简单实现:

string::iterator string::begin()
{
	return _str;
}
string::iterator string::end()
{
	return _str + _size;
}

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

3.string对象的修改接口的实现

string的修改大致分为增、删、查、改,下面我将逐个介绍他们的模拟实现。

在实现上述接口之前,我们首先来实现reserve这个函数,有了这个函数,我们可以实现string的扩容,代码如下所示:

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

		_str = tmp;
		_capacity = n;
	}
}

其逻辑也非常简单,如果需要预留空间比容量大,就需要扩容,创建一个新的空间,将原来的内容拷贝,释放旧空间,再将新空间指向旧空间。

接下来就是尾插和追加字符串,代码如下所示:

void string::push_back(char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	_str[_size] = ch;
	_str[_size + 1] = '\0';
	_size++;
}
void string::append(const char* str)
{
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _capacity);
	}
	strcpy(_str + _size, str);
	_size += len;
}

 其逻辑基本相似,空间足够就插入,空间不够就扩容。

有了上述两个接口之后,我们可以通过复用,很轻松就可以写出operator+=函数,代码如下所示:

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

接下来就是insert在任意位置插入函数的代码

void string::insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	size_t end = _size + 1;
	for (size_t i = end; i > pos; i--)
	{
		_str[i] = _str[i - 1];
	}
	_str[pos] = ch;
	_size++;
}
void string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}
	size_t end = _size + len;
	for (size_t i = end; i > pos + len - 1; i--)
	{
		_str[i] = _str[i - 1];
	}
	memcpy(_str + pos, str, len);
	_size += len;
}

分析上述代码,insert函数也分为插入一个字符串和一个字符两种,其中的逻辑与前面类似,都是空间足够就插入,空间不够就扩容,但是值得注意的是,我们在插入之前需要挪动数据,其逻辑就是,将前一个字符向后挪动,直到pos位置停止,再将目标字符或字符串插入pos位置。

字符串的删除,代码如下所示:

void string::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (_size - pos <= len)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

我们将字符串的删除分为两种情况,如果要删除的长度大于字符串剩余长度,则直接在pos位置补\0,如果小于剩余长度,则将要删除以后位置的字符串拷贝即可。

回到erase函数的定义里面,我们发现是给了缺省值npos的,这个npos是static修饰的,因此我们需要在类外面定义,传这个缺省值的目的就是,如果我们没有指定要删除多少,那么直接就从pos位置删到结束为止。

const size_t string::npos = -1;

 截取字符串函数substr函数的实现:

string string::substr(size_t pos, size_t len)
{
	if (_size - pos <= len)
	{
		string sub(_str + pos);
		return sub;
	}
	else
	{
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return _str;
	}
}

 分析上述代码,由于sbstr需要返回一个新的字符串,所以我们需要创建一个新的字符串接收截取的字符串,当需要截取的长度大于目标位置之后的长度,则直接返回剩余字符串;如果小于,则需要一个一个插入新创建的字符串。

最后则是字符串查找函数,代码如下所示:

size_t string::find(char ch, size_t pos)
{
	assert(pos < _size);
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;
}
size_t string::find(const char* str, size_t pos)
{
	assert(pos < _size);
	char* p = strstr(_str + pos, str);
	return p - _str;
}

字符串查找也分为两种,其中查找字符串我们使用了strstr字符串查找函数,由于find函数的返回值是下标位置,但是strstr函数返回的是指针,为了找到下标,我们使用了指针减指针得到下标。与删除类似,我们在定义的时候也传了缺省值npos。

4.string的一些其他接口

库里面的string是支持字符串比较的,其模拟实现如下所示:

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

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

bool string::operator<=(const string& s) const
{
	return *this < s || *this == s;
}

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

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

bool string::operator!=(const string& s) const
{
	return !(*this == s);
}

我们只需要实现其中任意两个接口,其他复用即可

库里的string实现了字符串的交换,其代码如下所示:

void string::swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

最后则是流插入和流提取函数的实现:

istream& operator>> (istream& is, string& str)
{
	str.clear();
	char ch = is.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = is.get();
	}

	return is;
}

ostream& operator<< (ostream& os, const string& str)
{
	for (size_t i = 0; i < str.size(); i++)
	{
		os << str[i];
	}

	return os;
}

string的模拟实现代码汇总如下:

const size_t string::npos = -1;
string::iterator string::begin()
{
	return _str;
}
string::iterator string::end()
{
	return _str + _size;
}

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

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

		_str = tmp;
		_size = str._size;
		_capacity = str._capacity;
	}
	return *this;
}
string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

size_t string::size()const
{
	return _size;
}
char& string::operator[](size_t pos)
{
	assert(pos < _size);

	return _str[pos];
}
const char& string::operator[](size_t pos)const
{
	assert(pos < _size);

	return _str[pos];
}

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

		_str = tmp;
		_capacity = n;
	}
}
void string::push_back(char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	_str[_size] = ch;
	_str[_size + 1] = '\0';
	_size++;
}
void string::append(const char* str)
{
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _capacity);
	}
	strcpy(_str + _size, str);
	_size += len;
}

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

void string::insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	size_t end = _size + 1;
	for (size_t i = end; i > pos; i--)
	{
		_str[i] = _str[i - 1];
	}
	_str[pos] = ch;
	_size++;
}
void string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}
	size_t end = _size + len;
	for (size_t i = end; i > pos + len - 1; i--)
	{
		_str[i] = _str[i - 1];
	}
	memcpy(_str + pos, str, len);
	_size += len;
}
void string::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (_size - pos <= len)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

size_t string::find(char ch, size_t pos)
{
	assert(pos < _size);
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;
}
size_t string::find(const char* str, size_t pos)
{
	assert(pos < _size);
	char* p = strstr(_str + pos, str);
	return p - _str;
}

void string::swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
string string::substr(size_t pos, size_t len)
{
	if (_size - pos <= len)
	{
		string sub(_str + pos);
		return sub;
	}
	else
	{
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return _str;
	}
}

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

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

bool string::operator<=(const string& s) const
{
	return *this < s || *this == s;
}

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

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

bool string::operator!=(const string& s) const
{
	return !(*this == s);
}

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

istream& operator>> (istream& is, string& str)
{
	str.clear();
	char ch = is.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = is.get();
	}

	return is;
}

ostream& operator<< (ostream& os, const string& str)
{
	for (size_t i = 0; i < str.size(); i++)
	{
		os << str[i];
	}

	return os;
}

以上就是string常见函数的模拟实现,感谢您能够看完,如果我的文章对于您有所帮助的话希望留下宝贵的点赞收藏加关注,您的支持就是对我创作的最大鼓励。

Rebuild started: Project: Project *** Using Compiler &#39;V6.22&#39;, folder: &#39;E:\Keil_v5\ARM\ARMCLANG\Bin&#39; Rebuild target &#39;Target 1&#39; assembling startup_stm32f10x_md.s... Start/core_cm3.c(445): error: non-ASM statement in naked function is not supported 445 | uint32_t result=0; | ^ Start/core_cm3.c(442): note: attribute is here 442 | uint32_t __get_PSP(void) __attribute__( ( naked ) ); | ^ Start/core_cm3.c(465): error: parameter references not allowed in naked functions 465 | "BX lr \n\t" : : "r" (topOfProcStack) ); | ^ Start/core_cm3.c(461): note: attribute is here 461 | void __set_PSP(uint32_t topOfProcStack) __attribute__( ( naked ) ); | ^ Start/core_cm3.c(479): error: non-ASM statement in naked function is not supported 479 | uint32_t result=0; | ^ Start/core_cm3.c(476): note: attribute is here 476 | uint32_t __get_MSP(void) __attribute__( ( naked ) ); | ^ Start/core_cm3.c(499): error: parameter references not allowed in naked functions 499 | "BX lr \n\t" : : "r" (topOfMainStack) ); | ^ Start/core_cm3.c(495): note: attribute is here 495 | void __set_MSP(uint32_t topOfMainStack) __attribute__( ( naked ) ); | ^ 4 errors generated. compiling core_cm3.c... compiling misc.c... compiling system_stm32f10x.c... compiling stm32f10x_adc.c... compiling stm32f10x_dac.c... compiling stm32f10x_exti.c... compiling stm32f10x_dbgmcu.c... compiling stm32f10x_dma.c... compiling stm32f10x_crc.c... compiling stm32f10x_cec.c... compiling stm32f10x_bkp.c... compiling stm32f10x_can.c... compiling stm32f10x_flash.c... compiling stm32f10x_pwr.c... compiling stm32f10x_fsmc.c... compiling stm32f10x_
03-31
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编码的傅同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值