STL详解--string的应用和模拟实现

目录

1.什么是STL

1.1概念

1.2.STL的六大组件

2.string类的基本概念和使用

2.1string类的介绍

2 1. string类对象的常见构造

2.2 . string类对象的析构

2.3. string类对象的容量操作

2.3.1容量

 2.3.2扩容

2.4string类中operator[]

2.5string类与迭代器

2.5.1正向迭代器

2.5.2反向迭代器

2.5.3使用迭代器的意义

2.5.4其他遍历方式

2.6string类的增删

2.6.1增

2.6.2查与匹配

 2.7string类中自定义插入与删除

3.string的模拟实现

3.1简易的string类的实现

3.1.1构造函数

3.1.2析构函数

3.1.3拷贝构造

**浅拷贝 

**深拷贝

3..1.4=运算符重载

3.1.5简易的string实现

3.1.6简易string的现代写法

3.2 string类的模拟实现

3.2.1迭代器的实现

3.2.2容量操作

*reserve

*resize

3.2.3字符串的插入函数

*push_back

*append

*insert

*运算符+=的重载

3.2.4字符串的删除

 3.3.5字符串的查找

3.3.6字符串的输入输出

3.3.7其他函数

3.2完整的string类


1.什么是STL

网上说学到了STL,才算是开始学习c++,如果学完了c++你说你会不用STL,你都不敢说你会c++,所以说STL在c++中的作用可以说是举足轻重的。

1.1概念

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。

1.2.STL的六大组件

2.string类的基本概念和使用

2.1string类的介绍

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。

2 1. string类对象的常见构造

1.string() (重点) 构造空的string类对象,即空字符串

2.string(const char* s) (重点) 用C-string来构造string类对象

3.string(size_t n, char c) string类对象中包含n个字符c

4.string(const string&s) (重点) 拷贝构造函数

 
	string s1; //定义出来什么都不弄,但是里面的容量自动capacity定义为15
	string s2("hello world");//定义s2给它赋值
	string s3(s2);
	string s4(s2, 2, 6);//只取a[2]~a[6]的字符串
	cout << s4 << endl;
	string s5(s2, 2, 10000);//取a[2]~到后一百的字符串,但是到了‘/0’就自动停止
	cout << s5 << endl;
	string s6("hello world", 3);//只取前三个字符
	cout << s6 << endl;

2.2 . string类对象的析构

自动调用,不用管

2.3. string类对象的容量操作

1. size(重点) 返回字符串有效字符长度

2.length 返回字符串有效字符长度

3.capacity 返回空间总大小

4.empty (重点) 检测字符串释放为空串,是返回true,否则返回false

5.clear (重点) 清空有效字符

6.reserve (重点) 为字符串预留空间

7.resize (重点) 将有效字符的个数该成n个,多出的空间用字符c填充

2.3.1容量

string s1("hello world");
	
	cout << s1.length()<<endl;//以‘/0’结束,它计算的是有效字符的长度
	cout << s1.size()<< endl;
	cout << s1.capacity() << endl;//显示容量,一般都是15
	s1.clear();//清除s1中的有效字符
	cout << s1 << endl;

 2.3.2扩容

   string s1("hello world");
	cout << s1.capacity() << endl;
	
	s1.reserve(100);//申请100个空间
	cout << s1.capacity() << endl;

 这里我们发现它申请并不是直接就申请了100个,而是在原来的字符串的有效长度增加了100个空间

    string s1;
  	s1.reserve(100);//这里就是申请100个空间

	string s2;
	s2.resize(100, 's');//这里不仅是申请了空间而且还初始化了

  这里的resize()可以初始化,如果原来的字符串有字符再进行扩容初始化,那么原来串里面的数据不会被覆盖。

如果我们开辟的空间小于字符串的本身呢?s1.resize(2),那么久只保存前面两个字符,如果是s1.reserve(2)那么它的值是不会变的。

2.4string类中operator[]

operator[] (重 点) 返回pos位置的字符,const string类对象调用

我们可以用s[i]进行读和写或者来进行其他操作。

    string s1("hello world");
	cout << s1 << endl;
	//遍历字符串
	for (size_t i = 0; i <s1.size(); ++i)
	{
		cout << s1[i] << " ";
	}
   
    for (size_t i = 0; i <s1.size(); ++i)
	{
        s1[i] += 1;//给字符串的每个字符的ASCII码+1
		cout << s1[i] << " ";
		
	}

2.5string类与迭代器

2.5.1正向迭代器

begin+ end

begin()获取一个字符串开始的位置 + end()获取最后一个字符下一个位置的

rbegin + rend

rbegin()获取最后一个字符 + rend()获取开头一个字符上一个位置

迭代器我们可以认为就是指针,使用的函数是 iterator

     string s1("hello world");
	 cout << s1 << endl;    
     string::iterator it = s1.begin();
    //auto it= s1.begin();//这里的auto可以自动识别类型
	while (it != s1.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;
	//修改
	it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

这里的it相当于就是指向字符‘h’这个位置的指针,然后往后面遍历。

2.5.2反向迭代器

函数是reverse_iterator

    string s1("hello world");
    //string::const_iterator it = s.begin();
	auto it = s1.begin();
	while (it != s1.end())
	{
		//*it -= 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//auto rit = s.rbegin();
	string::const_reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		//*rit = 'A';
		cout << *rit << " ";
		//这里的rit取的最后一个字符的地址,我们主观的以为要rit--,其实是rit++。
		++rit;
	}
	cout << endl;

注意:我们定义的反向迭代器rit之后,在进行遍历的时候虽然是从后往前,但是rit还是需要++操作。

2.5.3使用迭代器的意义

我们看了之前的 [] 操作符,感觉比迭代器简单太多了,为什么还要有迭代器呢?

这是因为对于string类来说,下标[]就足够的好用,但是其他的容器,比如list,map,set是不支持下标[]遍历的,因为像链表这些东西来说,它的物理内存不是连续的,所以就不能用下标[]。

2.5.4其他遍历方式

范围for

    //范围for,自动往后迭代,遇到‘/0’结束	
    for (auto& i : s1)//auto自动识别类型
	{
		i += 1;
		cout << i;
	}
	cout << endl;

 范围for会把s1中的每个字符都赋给i,自动往后迭代,自动判断结束。

s.at()

//给字符串的每个字符的ASCII码+1;这里的s1[i]=s1.at(i),
	for(size_t i = 0; i <s1.size(); ++i)
	{
		s1.at(i) += 1;
	}

这里的效果和上面相同

2.6string类的增删

push_back 在字符串后尾插字符c

append 在字符串后追加一个字符串

operator+= (重点) 在字符串后追加字符串str c_str(重点) 返回C格式字符串

find + npos(重点) 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置

rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置

substr 在str中从pos位置开始,截取n个字符,然后将其返回

2.6.1增

 string s1("hello world");
	s1.push_back('a');//在末尾插入字符a
	cout << s1 << endl;
	s1.append("bcd");//在末尾插入字符串bcd
	cout << s1 << endl;
	s1 += 'e';//在末尾插入字符e
	cout << s1 << endl;
	s1 += "hello linux";//在末尾插入字符串hello linux
	cout << s1 << endl;

 如果原字符串的空间满了,再push_back或者append,那么他会自动扩容。

 注意:这里还是推荐+=来尾插。

2.6.2查与匹配

string s("bit.txt"); 
	//这里用rfind()是从后面开始找,找到了'.'这个位置的下标,这里也可以用find(因为只有一个‘.’)
	size_t pos1 = s.rfind('.');
	//这里的npos的值是49亿多,这里就是验证得到的pos是否有效
	if (pos1 != string::npos)
	{
		//这里就是substr()就是从pos位置开始取s1.size()-pos个字符,就是后缀的长度。
		string suffix = s.substr(pos1, s.size() - pos1);
			cout << suffix<<endl;
	}

find会返回第一次出现匹配的位置,如果匹配失败会返回nops(无符号-1,表示一个极大的数)。
substr是字符串截取函数,意思是截取pos位置与file.size()-pos位置的字符串。同上,我们也可以使用rfind进行倒着查找。


	string url("http://www.cplusplus.com/reference/string/string/find/");
	size_t pos2 = url.find(':');//从0开始找到‘:’
	string s1 = url.substr(0, pos2);
	cout << s1<<endl;
	
	size_t pos3 = url.find('/',pos2+3);//从pos2+3的位置开始找
	string s2 = url.substr(pos2+3,pos3-(pos2+3));
	cout << s2 << endl;

	string s3 = url.substr(pos3 + 1);//从pos3+1位置找到结束
	cout << s3 << endl;

 我们都知道网址分为三部分:协议,域名与网络路径。下面使用find与substr来进行截取操作。

 2.7string类中自定义插入与删除

string s1("hello woeld");
	s1 += "++++";//在末尾插入字符串"++++"
	s1 += '-';//在末尾插入字符'-'
	cout << s1 << endl;
	//头插的三种方法(两个是插字符,一个是插字符串),尽量少用,效率低
	s1.insert(0,2,'s');//在第0个位置插入两个字符’s‘
	cout << s1 << endl;
	s1.insert(s1.begin(), 's');
	cout << s1 << endl;
	s1.insert(0,"xxx");
	cout << s1<<endl;

	//中间插入
	s1.insert(4, "sss");
	cout << s1 << endl;

在使用erase时,如果不给值就会删完,尽量少使用头部和中间的删除,因为要挪到数据,效率较低。

3.string的模拟实现

3.1简易的string类的实现

3.1.1构造函数

写简易的string类必须写构造函数,拷贝构造,析构函数,运算符重载。

class string
	{
	public:
		string(const char* _s1) 
			:_str(new char[strlen(_s1)+1])
		{
			strcpy(_str,_s1);
		}

	private:
		char* _str;
	};


	
void test1()
	{
		string s1("hello world");
   }

这样写构造函数运行起来没有什么问题,但是不排除以后人这样定义

string s4;

那么构造函数就要改一下。

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

3.1.2析构函数

~string()//析构函数
		{
			delete[] _str;//释放空间
			_str = nullptr;//指针指向空,防止野指针

		}

3.1.3拷贝构造

string s2(s1); //因为这里在类里没有合适的拷贝构造函数,会调用系统生成的,
		       //但是系统生成的拷贝构造是浅拷贝

这样定义那么我们写的构造函数就不够用了,那么可以用编译器自己实现的吗?

 我们发现是可以的,值是拷贝上了,但是他们两个的地址是却是一样的,那么这就会造成一个问题,他们会析构两次。

我们看到就会发生奔溃。string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,其中s2先析构,s1的值就别成了随机值,然后再析构就奔溃了。

**浅拷贝 

为什么系统自己生成的拷贝构造就不行呢?

因为编译器生成的拷贝构造函数属于是浅拷贝,他对自定义类型会进行按字节序拷贝,所以它就把地址也拷贝下来了,对于很多个对象指向同一块空间,如果其中的一个销毁了,其他的对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

**深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情 况都是按照深拷贝方式提供。就是重新开一个和s1相同的空间,然后把s1里的值拷贝给s2

string(const string& _s2)//这里就是进行的深拷贝,就是重新开一个和s1相同的空间,
			                      //然后把s1里的值拷贝给s2

			:_str(new char[strlen(_s2._str) + 1])
		{
			strcpy(_str, _s2._str);
		}

 我这时就可以看到,虽然值相同但是空间不相同,所以析构时候也不会有什么问题。

3..1.4=运算符重载

string s1("hello world");
string s3("travis Scott");
s3 = s1;
string &operator=(const string&_s3)
		{
			if (this != &_s3)
			{
				delete[]_str;
				_str = (new char[strlen(_s3._str) + 1]);
				strcpy(_str, _s3._str);

				return *this;
			}
			return *this;
		}

 这个由于之前讲过的,所以就不多介绍了。

3.1.5简易的string实现

class string
	{
	public:
		string(const char* _s1="")
			:_str(new char[strlen(_s1)+1])
		{
			strcpy(_str,_s1);
		}

		string(const string& _s2)//这里就是进行的深拷贝,就是重新开一个和s1相同的空间,
			                      //然后把s1里的值拷贝给s2

			:_str(new char[strlen(_s2._str) + 1])
		{
			strcpy(_str, _s2._str);
		}

		string &operator=(const string&_s3)
		{
			if (this != &_s3)
			{
				delete[]_str;
				_str = (new char[strlen(_s3._str) + 1]);
				strcpy(_str, _s3._str);

				return *this;
			}
			return *this;
		}

		~string()//析构函数
		{
			delete[] _str;//释放空间
			_str = nullptr;//指针指向空,防止野指针

		}

	private:
		char* _str;
	};

3.1.6简易string的现代写法

3.2 string类的模拟实现

3.2.1迭代器的实现

其他的迭代器可能实现起来比较困难,但是string类的迭代器算是简单的。首先我们要对迭代器重命名,因为我们是要自己写。

typedef char* iterator;
typedef const char* conts_iterator;

然后就算我们的迭代器的函数。

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

const iterator begin()const
{
	return _str;
}

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

size_t size()const
{
	return _size;
}		

我们需要注意的是cont修饰的this指针是不可修改的。

3.2.2容量操作

库里还有两个容量函数resize和reserve

*reserve

就是申请空间不做处理

void reserve(size_t newcapcity)
		{
			//如果新空间大于原来的,就从新创建一个
			if (newcapcity > _capacity)
			{
				char *temp = new char[newcapcity];
				strcpy(temp, _str);
				//释放原来的空间,使用新的
				delete[] _str;
				_str = temp;
			    _capacity = newcapcity;
			}

		}

如果传的值比原串的capacity大

 如果是小呢?

 是没有什么变化的,这和我们平时调用的普通的string是一样的,但是这个增容的大小还是有点区别的。

*resize

void resize(size_t newsize,char ch='\0')
		{
			if (newsize > _size)
			{
				/ 如果newSize大于底层空间大小,则需要重新开辟空间
				if (newsize>_capacity)
				{
					reserve(newsize);
					
				}
				memset(_str+_size,ch,newsize-_size);
			}
			_size = newsize;
			_str[newsize] = '\0';
			
		}

先resize检查容量够不够,不够就复用reserve函数,如果够这里可以看到的是如果我们增容的100个空间,后面新增的空间也被初始化成了l。

 这里增容的空间比原字符串小,那么就会保存前几个字符。

3.2.3字符串的插入函数

插入函数分为尾部插入单个字符(push_back),尾部插入字符串(append),任意位置插入字符串(insert),运算符重载(+=)。

*push_back

void push_back(char ch)
		{
			if (_size == _capacity)
			{
				//三目运算符
				int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}

			_str[_size++] = ch;
			_str[_size] = '\0';//字符串默认结尾有‘\0’
		}

直接在后面插入字符时,还要加‘\0’。

*append

void append(const char *s)
		{
			int len = strlen(s);

			if (_size + len > _capacity)//说明空间不够,要扩容
			{
				reserve(_size + len+1);//必须还要给‘\0’开一个空间,不然析构的时候会出错,所以要+1
			}
			strcpy(_str + _size, s);
			_size += len;
		}

		//重载一个string类型
		//void append(const string& s)
		//{
		//	if (_size + s._size > _capacity)//说明空间不够,要扩容
		//	{
		//		reserve(_size + s._size);
		//	}
		//	strcpy(_str + _size, s._str);
		//	_size += s._size;
		//}

这里一共写了两个版本,完成的功能都是一样的。

*insert

string& insert(size_t pos,char* s)
		{
			size_t len = strlen(s);

			if (_size + len > _capacity)//说明空间不够,要扩容
			{
				reserve(_size + len+1);//必须还要给‘\0’开一个空间,不然析构的时候会出错,所以要+1
			}

			size_t end= _size + len;
			while (end > pos)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			
			return *this;
		}
string s1("hello world");
s1.insert(0, "lol");//在0这个位置插入lol

 但是insert的效率特别的低,因为你要找到你要插入的位置,把后面的字符串往后移,然后再覆盖。

*运算符+=的重载

string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
string& operator+=(const char* s)
        {
	       append(s);
		   return *this;
        }
string s1("hello world");
s1 += "hello ";

3.2.4字符串的删除

string& erase(size_t pos=0,size_t len=-1)
		{
			if (len == -1 || pos+len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
				return *this;
			}

			strcpy(_str + pos, _str +pos+len);
			_size -= len;
			return *this;
		}
string s1("hello world");
s1.erase(0, 2);

 3.3.5字符串的查找

size_t find(char ch)
		{
			for (size_t i = 0; i < _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;
			}
		}

3.3.6字符串的输入输出

这里的内容上一节已经说过,这里就不多说了,就是要在类外定义

ostream& operator<<(ostream& out, const string& s)
	{
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		return out;
	}
iostream& operator>>(iostream& in,string& s)
	{
		s.clear();

		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}

		return in;
	}

3.3.7其他函数

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

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

		bool operator>(const string& s)
		{
			return strcmp(this->_str, s._str) > 0;
		}

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

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

		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
		//以上是比较运算符重载

		//其它操作
		const char* c_str()const//返回char*字符串
		{
			return _str;
		}

		void swap(string& s)//交换两个字符串类
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

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

		void clear()//清楚字符串
		{
			_size = 0;
			_str[_size] = '\0';
		}

3.2完整的string类

class string
	{
	public:
		/
		//迭代器
		typedef char* iterator;
		typedef const char* conts_iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const iterator begin()const
		{
			return _str;
		}

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

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capacity;
		}

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

		
		//reserve和resize
		void reserve(size_t newcapcity)
		{
			//如果新空间大于原来的,就从新创建一个
			if (newcapcity > _capacity)
			{
				char *temp = new char[newcapcity];
				strcpy(temp, _str);
				//释放原来的空间,使用新的
				delete[] _str;
				_str = temp;
				_capacity = newcapcity;
			}

		}

		void resize(size_t newsize, char ch = '\0')
		{
			if (newsize > _size)
			{
				/ 如果newSize大于底层空间大小,则需要重新开辟空间
				if (newsize > _capacity)
				{
					reserve(newsize);

				}
				memset(_str + _size, ch, newsize - _size);
			}
			_size = newsize;
			_str[newsize] = '\0';

		}

		///
		//插入函数
		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				//三目运算符
				int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}

			_str[_size++] = ch;
			_str[_size] = '\0';//字符串默认结尾有‘\0’
		}

		void append(const char *s)
		{
			int len = strlen(s);

			if (_size + len > _capacity)//说明空间不够,要扩容
			{

				reserve(_size + len + 1);//必须还要给‘\0’开一个空间,不然析构的时候会出错
			}
			strcpy(_str + _size, s);
			_size += len;
		}
		//重载一个string类型
		//void append(const string& s)
		//{
		//	if (_size + s._size > _capacity)//说明空间不够,要扩容
		//	{
		//		reserve(_size + s._size+1);
		//	}
		//	strcpy(_str + _size, s._str);
		//	_size += s._size;
		//}

		string& insert(size_t pos, char* s)
		{
			size_t len = strlen(s);

			if (_size + len > _capacity)//说明空间不够,要扩容
			{
				reserve(_size + len + 1);//必须还要给‘\0’开一个空间,不然析构的时候会出错,所以要+1
			}

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

			return *this;
		}

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

		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}
		
		//删除
		string& erase(size_t pos = 0, size_t len = -1)
		{
			if (len == -1 || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
				return *this;
			}

			strcpy(_str + pos, _str + pos + len);
			_size -= len;
			return *this;
		}

		
		//查找
		size_t find(char* substr, size_t pos = 0)
		{
			const char* ans = strstr(_str + pos, substr);
			if (ans)
			{
				return ans - _str;
			}
			return -1;
		}

		//
		//创建对象
		string(const char* _s1 = "")
			:_str(new char[strlen(_s1) + 1])
			, _size(strlen(_s1))
			, _capacity(strlen(_s1))
			//, _capacity(_size)
		{
			strcpy(_str, _s1);
		}

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

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

		~string()//析构函数
		{
			delete[] _str;//释放空间
			_str = nullptr;//指针指向空,防止野指针
			_size = _capacity = 0;
		}

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

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

		bool operator>(const string& s)
		{
			return strcmp(this->_str, s._str) > 0;
		}

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

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

		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
		//以上是比较运算符重载

		//其它操作
		const char* c_str()const//返回char*字符串
		{
			return _str;
		}

		void swap(string& s)//交换两个字符串类
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

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

		void clear()//清楚字符串
		{
			_size = 0;
			_str[_size] = '\0';
		}

	private:
		char* _str;
		size_t _capacity;//有效字符,不算\0
		size_t _size;

	};


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

	iostream& operator>>(iostream& in,string& s)
	{
		s.clear();

		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}

		return in;
	}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值