【C++】string

1.为什么学习string类?

1.1C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列 的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户 自己管理,稍不留神可能还会越界访问。

2.标准库中的string类

2.1string类介绍

string类文档介绍

在使用string类时,必须包含#include<string>头文件以及using namespace std;

注意:以下关于string的介绍都是讲解常用的,个别不常用的暂时不介绍了

2.2 auto与范围for

auto关键字

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

用auto声明指针类型时,用auto和auto*没有任何区别 ,但用auto声明引用类型时则必须加&

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

auto不能直接用来声明数组

cpp 复制代码
//auto 自动推导类型
auto x = 10;
auto y = 10.1;
cout << x << endl;
cout << y << endl;

//声明引用
int& z = x;
//auto m = z;
auto& m = z;//引用
m++;

//声明指针无区别
auto p1 = &x;
auto* p2 = &x;

auto aa = 1, bb = 2;
// 编译报错:error C3538: 在声明符列表中,"auto"必须始终推导为同一类型
auto cc = 3, dd = 4.0;


// 不能做参数
void func2(auto a)
{}

// 可以做返回值,但是建议谨慎使用
auto func3()
{
 return 3;
}

auto在c++中是一个非常便捷的工具,可以用他简化代码

cpp 复制代码
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
     std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange", 
"橙子" }, {"pear","梨"} };
 // auto
 //std::map<std::string, std::string>::iterator it = dict.begin();
     auto it = dict.begin();
     while (it != dict.end())
     {
         cout << it->first << ":" << it->second << endl;
         ++it;
     }
    return 0;
}
范围for
  • 对于一个有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号" :"分为两部分第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
cpp 复制代码
	//自动取容器数据 赋值给ch
	//自动判断结束
	//自动迭代
	//本质底层也会替换成迭代器,auto e = *迭代器

int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    // C++98的遍历
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
   {
        array[i] *= 2;
   }
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
   {
        cout << array[i] << endl;
   }
    // C++11的遍历
    for (auto& e : array)
        e *= 2;
    for (auto e : array)
        cout << e << " " << endl;
    string str("hello world");
    for (auto ch : str)
   {
        cout << ch << " ";
   }
    cout << endl;
 return 0;
}

2.3string的常用接口

2.3.1string类对象的常见构造
cpp 复制代码
string s1;                      //构造对象
string s2("Hello world");       //调用构造函数初始化
string s3(s2);                  //复制
string s4(s2, 1, 6);            //复制s2中第1个字符至第六个(下标从0开始)
string s5(s2, 3, 60);           //同上,当s2不足60个字符时,自动暂停
string s6(s2, 1);               //从s2的第一位开始复制
const char* str = "hello world";
string s7(str, 5);              //复制str字符串的前5个字符
string s8(100, '#');            //复制100个字符'#'
2.3.2string类对象的容量操作
cpp 复制代码
int main()
{
	string s;
	int n = 100;
	s.size();		 //返回字符串有效字符长度
	s.length();		 //返回字符串有效字符长度
	s.capacity();	 //返回空间总大小
	s.empty();		 //检测字符串释放为空串,是返回true,否则返回false
	s.clear();		 //清空有效字符
	s.reserve();     //为字符串预留空间**
	s.resize(n,'c'); //将有效字符的个数该成n个,多出的空间用字符c填充
	return 0;
}

注意:

1.size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。

  1. clear()只是将string中有效字符清空,不改变底层空间大小。

  2. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数 增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

  3. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参 数小于string的底层空间总大小时,reserver不会改变容量大小。

容量的查找等:

cpp 复制代码
void test_string04()
{
	string s1("12345");
	cout << s1.max_size() << endl;//返回能储存的最大数据,知道干啥的就行(没啥用)
	//size()与capacity()均不包含"\0"
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	string s2;
	size_t old = s2.capacity();
	cout << "old capacity:" << old << endl;
	for (size_t i = 0; i < 100; i++)
	{
		s2.push_back('x');
		if (s2.capacity() != old)
		{
			cout << "capacity:" << s2.capacity() << endl;
			old = s2.capacity();
		}
	}

	//不会缩容
	cout <<"empty:" << s1.empty() << endl;
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	cout << s1 << endl;
	s1.clear();
	cout << s1 << endl;
	cout <<"empty:" << s1.empty() << endl;
	cout <<"size:"<< s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;

	//不会缩容
	cout <<"empty:" << s2.empty() << endl;
	cout << "size:" << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;
	cout << s2 << endl;
	//s2.clear();
	for (size_t i = 0; i < 100; i++)
	{
		s2.pop_back();
	}
	cout << s2 << endl;
	cout <<"empty:" << s2.empty() << endl;
	cout <<"size:"<< s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;


}

string在push_back时会自动扩容,扩容的规律根据编译平台变换。

通过测试发现,clear并不会把原字符串的内存容量删掉,而是把size值清空。

pop_back也一样。通过改变size值达到删除数据的效果。

容量的修改:

这里要注意区分:

reserve 储备 保留
reverse 反转 逆置

capacity的修改:

cpp 复制代码
void test_string05()
{
	string s1("123456");
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	//主要用来扩容 提高效率(确定需要多少空间)
	s1.reserve(100);
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl<<endl;
	s1.reserve(10);
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	//缩容不靠谱
	s1.reserve(3);
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
}

size的修改:

在修改size时,capacity也会随之变动。

cpp 复制代码
void test_string06()
{
	string s1("123456");
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl<<endl;

	//n>capacity>size
	s1.resize(20,'x');
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;

	//capacity>n>capacity
	s1.resize(25,'x');
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;

	//capacity>n>capacity
	s1.resize(25,'x');//插入的是\0
	s1.push_back('y');
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
}
2.3.3string类对象的访问及遍历操作

c++中引入的迭代器是一个非常重要的工具,它就相当于各大容器的指针。

那么先了解一下最普通的:

cpp 复制代码
string s1("Hello world");
int pos = 1;
s1[pos];   //返回pos位置的字符,const string类对象调用
s1.begin();//begin获取一个字符的迭代器
s1.end();  //end获取最后一个字符下一个位置的迭代器
cpp 复制代码
void Test_string02()
{
	string s1("Hello world");
	const string s2("Hello world");

	//遍历+修改
	//下标 + [ ](小众)
	s1[0]++; //s1.operator[](0)++

	cout << s1 << endl;
	
	// s1的每个字符都++
	for (int i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
	cout << s1 << endl;

	//[begin(),end())
	//迭代器(所有容器主流的编译方式)
	// s1的每个字符都--
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		(*it1)--;//修改内部数据
		it1++;   //迭代器往后走
	}
	cout << s1 << endl;
	cout << endl;
    
    //数据结构--动态顺序表
	vector<int>v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	vector<int>::iterator it2 = v.begin();
	while (it2 != v.end())
	{
		cout << *it2 << ' ';
		it2++;
	}
	cout << endl;

    //数据结构--链表
	list<int>lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);

	list<int>::iterator it3 = lt.begin();
	while (it3 != lt.end())
	{
		cout << *it3 << ' ';
		it3++;
	}
	cout << endl;

	//迭代器的意义
	//1:统一类似的方式遍历修改容器
	//2:算法脱离具体底层结构,和底层结构解耦

    //迭代器做参数
	reverse(s1.begin(), s1.end());//反转函数,目前认识即可
	reverse(v.begin(), v.end());
	reverse(lt.begin(), lt.end());

    //同时可以配合范围for使用
	//自动取容器数据 赋值给ch
	//自动判断结束
	//自动迭代
	//本质底层也会替换成迭代器,auto e = *迭代器
	for (auto& e : s1)
	{
		e--;
	}
	for (auto ch : s1) 
	{
		cout << ch << " ";
	}
	cout << endl;

	for (auto ch : v) 
	{
		cout << ch << " ";
	}
	cout << endl;

	for (auto ch : lt) 
	{
		cout << ch << " ";
	}
	cout << endl;

	int array[] = { 1,2,3,4,5 };
	for (auto e : array)
	{
		cout << e << " ";
	}
	cout << endl;

}

除此之外,还有比较特殊的迭代器,可以用于不同的情况。

cpp 复制代码
void test_string03()
{
	string s1("123456");
	const string s2("Hello world");

	string::iterator it1 = s1.begin();//普通正向迭代器
	while (it1 != s1.end())
	{
		(*it1)--;
		it1++;
	}
	cout << s1;
	cout << endl;

	string::const_iterator it2 = s2.begin();//const迭代器
	//string::const_iterator it2 = s2.cbegin();//const迭代器
	while (it2 != s2.end())
	{
		//(*it2)--;不能修改
		++it2;
	}
	cout << s2;
	cout << endl;

	string::reverse_iterator it3 = s1.rbegin();//反向迭代器
	while (it3 != s1.rend())
	{
		cout << *it3 << " ";
		it3++;
	}
	cout << endl;

	string::const_reverse_iterator it4 = s2.crbegin();//const反向迭代器
	while (it4 != s2.crend())
	{
		//不能修改
		//*it4 = 'x';
		cout << *it4 << " ";
		it4++;
	}
	cout << endl;
}
2.3.4string类对象的修改操作

注意:

  1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差 不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

cpp 复制代码
int main()
{
	string s;
	s.push_back('1');//在字符串后尾插字符c
	s += "23";       //在字符串后追加一个字符串
	s.append("45"); //在字符串后追加字符串str
}

在尾部添加字符时更常用"+=" 它不仅方便,而且还同时支持字符与字符串

cpp 复制代码
int main()
{
    //在尾部加字符
	string s1("Hello World");
	s1.push_back(' ');
	s1.append("hello bit");
	s1.append(10,'x');
	s1 += ' ';
	s1 += "hello";
	cout << s1 << endl << endl;
    
    return 0;
}

对于字符串中间的修改:

cpp 复制代码
int mian()
{
	string s;
	s.assign(); //赋值操作
	s.insert(); //从字符串中间插入字符串
	s.erase();  //从字符串中删除字符
	s.replace();//指定位置替换操作
}
cpp 复制代码
void test_string08()
{
	string s1 = 'X';
	cout << s1 << endl;
	s1.assign(10, 'x');//赋值
	cout << s1 << endl;

	//insert 插入字符串
	//insert谨慎使用 底层数据挪动,效率低下,O(N);
	string s2("Hello World");
	s2.insert(0, "yyy");
	cout << s2 << endl;

	s2.insert(0, 1, '!');
	cout << s2 << endl;

	s2.insert(s2.begin(), '!');
	cout << s2 << endl<<endl;

	string s3("hello Wolrd");
	s3.erase(5, 1);//第五个位置删一个字符
	cout << s3 << endl;
	s3.erase(5);//第五个位置后全部删除
	cout << s3 << endl << endl;

	string s4("hello   Wolrd");
	s4.replace(5, 1, "#");//第五位 替换一个
	cout << s4 << endl;
	s4.replace(5, 3, "#");//从第五位三个字符替换成"#"
	cout << s4 << endl;
	s4.replace(5, 1, "!!!!");//从第五位1个字符替换成"!!!!"
	cout << s4 << endl << endl;

}

除了对字符串的修改,还提供了具有"查找"功能的工具。

cpp 复制代码
int mian()
{
	string s;
	s.c_str(); //返回C格式字符串
	s.find(1);//从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
	s.rfind(2); //从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
	s.substr(0, 2);//在str中从pos位置开始,截取n个字符,然后将其返回
}
cpp 复制代码
void test_string04()
{
	//把空格全部替换成%%
	string s5("Hello  World Hello XiaoHai");
	//法一:
	size_t pos = s5.find(' ');//从起始开始返回第一个字符为' '的位置.
	while (pos != string::npos)//静态成员变量
	{
		s5.replace(pos, 1, "%%");
		pos = s5.find(' ',pos+2);
	}
	cout << s5 << endl;

	//法二:
	string s6;
	for (auto ch:s5)
	{
		if (ch == ' ')
		{
			s6 += "%%";
		}
		else
		{
			s6 += ch;
		}
	}
	cout << s6;
}
2.3.5string类非成员函数
cpp 复制代码
int main()
{
	string s;
	cin >> s;//输入运算符重载
	s + "wolrd";
	cout << s;//输出运算符重载

	string name;
	cout << "请输入名字: ";
	std::getline(std::cin, name);//获取一行字符串
	cout<<"hello:"<< name << "\n";

	string s1 = "hello";
	string s2 = "hellox";

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

	return 0;
}

3.string类型的实现

3.1经典的string类问题

cpp 复制代码
class String
{
public:
	/*String()
	:_str(new char[1])
	{*_str = '\0';}
	*/
	//String(const char* str = "\0") 错误示范
	//String(const char* str = nullptr) 错误示范
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
// 测试
void TestString()
{
	String s1("hello bit!!!");
	String s2(s1);
}

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

3.2浅拷贝:

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

3.3深拷贝:

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

3.4接口实现:

3.4.1string.h:
cpp 复制代码
//string.h
#pragma once
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace XiaoHai
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin();
		iterator end();

		const_iterator begin() const;
		const_iterator end() const;

		//string();
		string(const char* str = "");
		const char* c_str() const;
		~string();
		string(const string& s);
		size_t size() const;
		const char& operator[](size_t i)const;
		char& operator[](size_t i);

		void reserve(size_t n);

		//插入
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);

		string& insert(size_t pos, char ch);
		string& insert(size_t pos,const char*str);
		string& erase(size_t pos, size_t len = npos);

		void pop_back();

		size_t find(char ch, size_t pos = 0) const;
		size_t find(const char* str, size_t pos=0) const;

		string substr(size_t pos, size_t len = npos) const;

		bool operator<(const string& s) const;
		bool operator<=(const string& s) const;
		bool operator>(const string& s) const;
		bool operator>=(const string& s) const;
		bool operator==(const string& s) const;
		bool operator!=(const string& s) const;
		void clear();
	
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		static const size_t npos;
	};

	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
}
3.4.2string.cpp:
cpp 复制代码
//string.cpp
#include"string.h"
namespace XiaoHai
{
	//string::string()
	//	:_str(new char[1]{'\0'})
	//	,_size(0)
	//	,_capacity(0)
	//{ }

	string::string(const char* str)
		:_str(new char[strlen(str) + 1])
		, _size(strlen(str))
		,_capacity(strlen(str))
	{
		memcpy(_str, str, _size + 1);
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

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

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

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

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

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

	string::iterator string::begin()
	{
		return _str;
	}

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

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

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

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

	//插入
	void string::push_back(char ch)
	{
		if (_size >= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		if (ch != '\0')
		{
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len>_capacity)
		{
			size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;
			reserve(newcapacity);
		}

		//strcpy(_str+_size, str);
		memcpy(_str+_size, str, len + 1);
		_size += len;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}

	string& string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size>= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		//挪动数据
		//size_t end = _size;
		/*while (end>=(int)pos)
		{
			_str[end + 1] = _str[end];
			--end;
		}*/
		size_t end = _size+1;
		while (end>pos)
		{
			_str[end] = _str[end-1];
			--end;
		}
		_str[pos] = ch;
		++_size;
		return *this;
	}

	string& string::insert(size_t pos, const char*str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;
			reserve(newcapacity);
		}

		/*int end = _size;
		while (end >= (int)pos)
		{
			_str[end + len] = _str[end];
			--end;
		}*/
		int end = _size+len;
		while (end >pos+len-1)
		{
			_str[end] = _str[end-len];
			--end;
		}

		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		} 
		_size += len;
		return *this;
	}

	string& string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		//要删除的数据,大于pos后面的字符个数
		//pos后面全删
		if (len==npos||len>=(_size-pos))
		{
			_size = pos;
			_str[_size] = '\0';
		}
		else
		{
			size_t i = pos + len;
			memmove(_str + pos, _str + i, _size + 1 - i);
			_size -= len;
		}
		return *this;
	}

	void string::pop_back()
	{
		assert(_size > 0);

		--_size;
		_str[_size] = '\0';
	}

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

	}

	string string::substr(size_t pos, size_t len) const
	{
		if (len == npos || len >= _size - pos)
		{
			len = _size - pos;
		}
		string ret;
		ret.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			ret += _str[pos + i];
		}
		return ret;
	}

	bool string::operator<(const string& s) const
	{
		rsize_t i1 = 0, i2 = 0;
		while (i1<_size&&i2<s._size)
		{
			if (_str[i1] < s[i2])
			{
				return true;
			}
			else if (_str[i1]>s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i2 < s._size;
	}
	bool string::operator<=(const string& s) const
	{
		return *this < s || *this == s;
	}
	bool string::operator>(const string& s) const
	{
		return !(*this <= s);
	}
	bool string::operator>=(const string& s) const
	{
		return !(*this < s);
	}
	bool string::operator==(const string& s) const
	{
		rsize_t i1 = 0, i2 = 0;
		while (i1 < _size && i2 < s._size)
		{
			if (_str[i1] != s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i1 == _size && i2 == s._size;
	}
	bool string::operator!=(const string& s) const
	{
		return !(*this == s);
	}

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

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

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
        char buff[128];
        int i = 0;
        char ch = _cin.get();
        while (ch != '\n' && ch != ' ')
        {
        	buff[i] = ch;
        	if (i == 127)
        	{
    	    	buff[i] = '\0';
    	    	s += buff;
    	    	i = 0;
	        }
	        ch = _cin.get();
    	    i++;
        }
        if (i > 0)
        {
        	buff[i] = '\0';
        	s += buff;
        }
        return _cin;
	}

    const size_t XiaoHai::string::npos = -1;
}

4.类型转换

4.1其他类型转string

c++中引用了新的函数to_string(),可以把其他类型转化为string字符串

4.2string转为其他类型

调用对应的函数即可把string转化为其他类型,比如stoi()就是string to int,转化为整数类型。