Error[8]: Undefined offset: 311, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 114
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

[+++]
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

[+++]
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

[+++]
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

[+++]
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

[+++]
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
[+++]
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
[+++]
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
[+++]
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
[+++]

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
[+++]

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 
[+++]

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

<===> <===><===>)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 312, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 114
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

[+++]
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

[+++]
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

[+++]
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

[+++]
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

[+++]
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
[+++]
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
[+++]
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
[+++]
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
[+++]

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
[+++]

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 
[+++]

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

<===><===>)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 313, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 114
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

[+++]
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

[+++]
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

[+++]
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

[+++]
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

[+++]
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
[+++]
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
[+++]
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
[+++]
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
[+++]

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
[+++]

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 
[+++]

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

<===>)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 15, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 
[+++]

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 16, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 
[+++]

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 17, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 

9.find()

find函数是正向查找第一个匹配的字符
 
[+++]

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 18, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 

9.find()

find函数是正向查找第一个匹配的字符
 

find函数是正向查找第一个匹配的字符串
 
[+++]

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 19, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 

9.find()

find函数是正向查找第一个匹配的字符
 

find函数是正向查找第一个匹配的字符串
 

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 
[+++]

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 20, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 

9.find()

find函数是正向查找第一个匹配的字符
 

find函数是正向查找第一个匹配的字符串
 

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
[+++] 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
【C++】STL——string类模拟实现_C_内存溢出

【C++】STL——string类模拟实现

【C++】STL——string类模拟实现,第1张

目录

前言

实现框架思维导图

一、默认成员函数 

1.构造函数

2.拷贝构造

1.传统写法

2.现代写法 

3.赋值运算符重载函数 

1.传统写法 

2.现代写法 

4.析构函数

二、容量相关的函数

1.reserve()

2.resize()

3.size()和 capacity()

三、字符串的增删查改函数

1.push_back()

2.append()

3.operator+=()

4.insert()

5.swap()

6.erase()

7.clear()

8.c_str()

9.find()

四、字符串访问函数

1.operator[ ]() 

2.迭代器 

五、关系运算符重载函数 

六、流提取、流插入运算符重载

完整代码


前言

        在之前的string类的介绍中,我们重点介绍了string类常用的接口函数及使用规则。相比我们在C语言学习阶段使用的字符串函数去解决相关的题目要轻松很多,但是轻松的背后却是大神们为我们建立好的基础;学好string类的基本用法使我们入门的关键,想要了解string类的背后原理,我们还需要去简单的造轮子;本篇文章将为大家讲解string类的模拟实现。

实现框架思维导图

一、默认成员函数 

        string的模拟实现无论是简单版还是稍健全版都需要默认成员函数:构造函数、拷贝构造、析构函数和赋值重载。

1.构造函数

        在实现构造函数时,我们将其设置为缺省参数;这样的好处就在于,无参构造时,将会默认构造出空字符串。

//构造函数
string(const char* str = "")
	:_size(strlen(str))  //使用初始化成员列表初始化
	, _capacity(_size)   //起始的空间大小和_size是一样的
{
	_str = new char[_capacity + 1]; 
    //为存储字符串开辟空间,这里多开一个空间是因为_capacity计算的是有效空间的长度,要给'//拷贝构造 --- 传统写法
//str2(str1)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);
}'预留一个空间
	strcpy(_str, str);
}
2.拷贝构造

对于拷贝构造,我们首先要了解一下深浅拷贝的概念:

浅拷贝:

        又称值拷贝,拷贝出来的对象和原来的对象同时指向了一块空间,当进行析构函数时,这个空间被释放了两次或多次;拷贝出来的对象的修改也会影响到原来的对象;

深拷贝:

        又称位拷贝,拷贝出来的对象与原对象的内容是一样的,但是属于另一块空间,这两块空间各自的 *** 作都不会影响到另一个空间;

1.传统写法

         传统写法的思路:开辟一个和原来对象同样的大的空间,然后将原对象的内容拷贝过去;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];//开辟一块和_str1一样的空间tmp
		strcpy(tmp, s._str);   //将_str1的数据拷贝给tmp
		delete[] _str;         //释放str2原来的空间
		_str = tmp;            //让str2指向新的空间
		_size = s._size;       //调整_size
		_capacity = s._capacity;//调整_capacity
	}
	return *this;
}
2.现代写法 

        现代写法的思路: 先去构造出一个和原来对象相同的tmp对象,然后将tmp对象与待拷贝对象数据进行交换;

//拷贝构造 --- 现代写法
//str2(str1)
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	//this->swap(tmp);
	swap(tmp);
}
3.赋值运算符重载函数 

        赋值重载和拷贝构造类似,也是通过一个已有对象构造新对象,也会涉及到深浅拷贝的问题

1.传统写法 

        赋值重载函数的传统写法:两个已有对象要完成赋值 *** 作(str2 = str1)我们可以开辟一个和str1同样大小的空间tmp,然后将其数据拷贝到tmp中,先将str2原来的空间进行释放,让str2指向tmp;

//赋值重载 --- 传统写法
//str2 = str1
string& operator=(string s)
{
	swap(s);
	return *this;
}
2.现代写法 

        赋值重载函数的现代写法: 采用了值传参而非引用传参,它会去调用构造函数,让构造函数来创建一个和str1一样对象s,再将其与待赋值的对象str2进行交换,达到赋值的目的,相比传统写法简单很多。

//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}
4.析构函数

        string类的析构函数需要我们自己去写,默认生成的析构函数是不会对堆上开辟的空间进行释放,我们使用的是new开辟空间的,为了规范使用,我们采用delete进行释放空间;

//有效数据个数
size_t size() const
{
	return _size;
}

//有效空间大小(//尾插字符
void push_back(char ch)
{
	if (_size == _capacity) //判断数据个数是否与容量相等
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2); //以2倍扩容
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = 'string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}
'; //末尾需要加上'void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}'

	//insert(_size, ch);或复用insert
}不算在内)
size_t capacity() const
{
	return _capacity;
}
二、容量相关的函数 1.reserve()

reserve增容:

        1.当 n > _capacity 时,将capacity扩大到n;

        2.当 n < _capacity 时,不进行任何操作;

模拟实现思路:

        1. 开辟一块n大小的空间tmp(要多开一个,给\0)

        2. 将原有数据拷贝到新开辟的空间tmp中

        3. 释放原来的空间,让原来指针指向新的空间

        4. 调整好现在的_capacity的大小

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}
2.resize()

resize增容:

        1.当 n <= _size 时,表明数据个数减少,但是容量不变(库中实现的也是如此)

        2.当 n > _size时:

                ①n > _capacity:需要增容,可以复用reserve函数,然后采用memset函数按字节设置

                ②n <= _capacity:不需要增容,直接memset

注意:因为字符串是有 '\0' 的,最后都需要添加一个 '\0' 

void resize(size_t n, char ch = '\0')
{
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '';
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, ch, n - _size);//内存设置:从_str+_size位置开始向后 n-_size个字节设置成ch
		_size = n;
		_str[_size] = '\0';
	}
}
3.size()和 capacity()

 size函数和capacity函数实现比较简单,返回的就是时时更新的数据个数和空间大小
 
//清空字符串 void clear() { _str[0] = '//返回C形式的字符串 const char* c_str() const { return _str; }'; _size = 0; }
三、字符串的增删查改函数 1.push_back()

        push_back函数的作用就是在当前字符串的尾部插入一个字符(不能是字符串)。插入字符,我们就需要对其容量进行判断,容量足够可以直接插入,容量不够则需要增容;我们一开始的容量是为0的,如果以2倍的方式增容,是不行的,我们要给到一个起始容量,可以采用三目运算符;
 
size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; }
2.append()

        append函数是用来尾插字符串的。也需要判断容量是否足够,当原有数据个数和需要追加的字符串个数之和大于_capacity是,需要增容;
 
//尾插字符串
void append(const char* str)
{
	size_t len = strlen(str);    //计算需要尾插的字符串的长度
	if (_size + len > _capacity) //不需要考虑给\0空间
	{
		reserve(_size + len);    //增容
	}
	strcpy(_str + _size, str);   //拷贝数据--连'\0'一起拷贝
	_size += len;

	//insert(_size, ch);或复用insert
}
3.operator+=()

        这个函数可以完成字符、字符串的尾插,尾插字符可以复用push_back函数,尾插字符串可以复用append函数;
 
size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
4.insert()

        insert函数可以用来在字符串的pos位置插入一个字符或字符串。既然是插入数据,当然也需要进行容量的判断,在pos位置插入字符时,其过程就是将pos位置及以后的字符向后挪动一位。
 
<= _size);   //检查pos是否合法(如:pos=-1)
	if (_size == _capacity) //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	size_t end = _size + 1; //定义后一个end指向''的下一个位置
	while (end >//在pos位置插入字符
string& insert(size_t pos, char ch)
{
	assert(pos 
pos) //找pos位置,未找到向后挪动数据 { _str[end] = _str[end - 1]; --end; } _str[pos] = ch;//找到了,插入字符 ++_size; //更新一下_size return *this; }

insert在pos插入字符串(len个),将pos及以后的字符向后挪动len个位置;

需要注意:在插入字符串时我们可以采用strncpy函数,不能使用strcpy函数,因为会将 '\0' 插入进去
 
 
<= _size);         //检查pos的合法性
	size_t len = strlen(s);       //计算待插入的字符串的有效长度
	if (_size + len >//在pos位置插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos 
_capacity) //判断是否需要增容 { reserve(_size + len); } size_t end = _size + len; //定义end在_size+len的位置 while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); //从pos位置开始拷贝,拷贝len个 _size += len; return *this; }

5.swap()

        模拟实现swap函数,为了避免自己实现的swap函数名和库当中的swap冲突,需要加上std::
 
//可读可写 char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } //可读不可写 const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }

6.erase()

erase函数是用来从pos位置开始删除n个字符串:

        1. 从pos位置开始向后全部删除;

        2. 从pos位置开始向后删除一部分;
 
 
< _size);
	if (len == npos || pos + len >string& erase(size_t pos = 0, size_t len = npos)//npos=-1(size_t)整形的最大值
{
	assert(pos 
= _size)//当len超过了有效字符个数或就是npos(从pos向后删除全部) { _str[pos] = '\0';//直接在pos位置加上'\0',就达到删除的目的,访问只能访问到'\0' _size = pos; //更新_size } else //删除一部分 { strcpy(_str + pos, _str + pos + len);//将需要保留的字符串去覆盖要删除的字符串 _size -= len; //更新_size } return *this; }

7.clear()

clear函数是用来字符串置空的,只需要将字符串的第一位置为'\0',再将_size置0;
 
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; }

8.c_str()

 c_str函数是用来返回C形式的字符串,可以直接返回对象的成员变量_str;
 

9.find()

find函数是正向查找第一个匹配的字符
 

find函数是正向查找第一个匹配的字符串
 

四、字符串访问函数 1.operator[ ]() 

[ ]运算符重载是为了让string类能够实现下标的访问
 

2.迭代器 

        相比其他容器的迭代器,string类的迭代器相对简单,实际上是char*的typedef;函数后面的const表示的是this不能被修改;
 
 
五、关系运算符重载函数  
<(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator==(const string& s1, const string& s2)
{
	return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//实现两种比较其他的可以复用
bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>< s2);
}

bool operator>bool operator<= s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}=(const string& s1, const string& s2)
{
	return !(s1 
(const string& s1, const string& s2) { return !(s1
 
六、流提取、流插入运算符重载 
<<
ostream& operator<<(ostream& out, const string& s)
{
	//方式一
	for (auto ch : s)
	{
		out << ch;
	}
	//方式二
	for (size_t i = 0; i < s.size(); ++i)
	{
		out << s[i];
	}
	//方式三 
	//out << s.c_str();//不能这样写
	return out;
}

//流提取 >//流插入 
> istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n')//利用循环可以连续输入 { s += ch; ch = in.get(); } return in; }
 
完整代码 
< _size);
			return _str[pos];
		}

        //[]重载 --- const
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

        //reserve增容
		void reserve(size_t n)
		{
			if (n ><= _size)
			{
				_size = n;
				_str[_size] = '< _size; ++i)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}
			return npos;
		}

		//查找第一个匹配的字符串
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		//在pos位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end >';
			}
			else
			{
				if (n >namespace mlxg3
{
	class string
	{
	public:
		/******************迭代器********************/
		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;
		}
		

		/******************构造函数********************/

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

		

		/******************拷贝构造********************/
		//s2(s1)
		//传统写法
		/*
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		*/
		

		/******************赋值重载********************/

		/*
		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);
		}
		//s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			//this->swap(tmp);
			swap(tmp);
		}

		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		

		/******************析构函数********************/
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		/******************增删查改、增容********************/
        //返回c形式的字符串
		const char* c_str() const
		{
			return _str;
		}
        //返回有效字符的个数
		size_t size() const
		{
			return _size;
		}

        //返回有效容量的大小
		size_t size() const
		{
			return _size;
		}

        //[]重载
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				char* tmp = new char[n + 1];//这里加1是为了给'\0'一个空间
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

        //resize增容
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size);
			if (len == npos || pos + len > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

        //尾插字符
		void push_back(char ch)
		{
			/*
			if (_size == _capacity)
			{
				//增容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
			*/
            
			insert(_size, ch);//复用
		}

		//尾插字符串 
		void append(const char* str)
		{
			/*
			size_t len = strlen(str);
			if (_size + len > _capacity)//不需要考虑给\0空间
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
			*/
			insert(_size, str);//复用
		}

		//尾插字符 --- +=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//尾插字符串 --- +=重载
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//查找第一个匹配的字符
		size_t find(char ch)
		{
			for (size_t i = 0; i <(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator> pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;
			return *this;
		}

		//在pos位置插入一个字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos < s2);
	}

	bool operator> _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		//从pos位置开始删除字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

    //流插入重载 
	ostream& operator<<(ostream& out, const string& s)
	{
		//方式一
		for (auto ch : s)
		{
			out << ch;
		}
		//方式二
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		//方式三 
		//out << s.c_str();//不能这样写
		return out;
	}

    //流提取重载 
	istream& operator>= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		//将字符串置空
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符的空间数
		static const size_t npos;
	};

    //定义npos(为了和库一致)
	const size_t string::npos = -1;

	/*
	   字符串s1      字符串s2
		"abcd"   和   "abcd"    ----false
		"abcd"   和   "abcde"   ----true
		"abcde"   和   "abcd"   ----false
	*/
    //字符串的比较 --- 关系运算符的重载
	bool operator=(const string& s1, const string& s2)
	{
		return !(s1 
(const string& s1, const string& s2) { return !(s1

>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1352434.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-14
下一篇 2022-06-14

发表评论

登录后才能评论

评论列表(0条)

保存