C++:string类的常用接口说明及其模拟实现

本文主要介绍string类和该类常用的接口,并根据接口功能对其进行模拟实现。

目录

一、string类的常用接口说明

1. string类对象的常见构造

2.string类对象的容量操作

3.string类对象的访问及遍历操作

4.string类对象的修改操作

5. string类非成员函数

二、string的模拟实现

1.默认成员函数和成员变量

浅拷贝与深拷贝

2.迭代器相关函数的实现

3.字符串大小与容量相关函数的实现

4.字符串修改操作相关函数的实现

5.字符串访问相关函数的实现

6.字符串比较相关函数的实现

7.流插入和流提取的实现

8.完整代码


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

一、string类的常用接口说明

(下面我们只介绍最常用的接口)

1. string类对象的常见构造

(文档链接)

函数名称 功能说明
string(); 构造空的string类对象,即空字符串
string (const string& str); 用C-string来构造string类对象
string (size_t n, char c); string类对象中包含n个字符c
string (const string& str); 拷贝构造函数

演示:

string(); //构造空的string类对象,即空字符串
string (const string& str);//用C-string来构造string类对象
string (size_t n, char c);//string类对象中包含n个字符c
string(const string&s) //拷贝构造函数

void StringTest()
{
	string s1; // 构造空的string类对象s1
	string s2("hello world"); // 用C格式字符串构造string类对象s2
    string s3(6,'x');//有6个'x'字符的字符串构造string类对象s3
	string s4(s2); // 拷贝构造s4
}

2.string类对象的容量操作

函数名称 功能说明
size 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty 检测字符串释放为空串,是返回true,否则返回false
clear 清空有效字符
reserve 为字符串预留空间
resize 将有效字符的个数该成n个,多出的空间用字符c填充

1.size 和 length:返回字符串有效字符长度

size_t size() const;
size_t length() const;
int main()
{
	string s1("hello world");
	cout << s1.size() << endl;//11
	cout << s1.length() << endl;//11
	
	return 0;
}

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

2.capacity:返回空间总大小

size_t capacity() const;
int main()
{
	string s1("hello world");
	string s2("C++");
	cout << s1.capacity() << endl;//15
	cout << s2.capacity() << endl;//15
	return 0;
}

返回当前对象中该字符串所分配的存储空间的大小。

3.empty:检测字符串释放为空串,是返回true,否则返回false

bool empty() const;
int main()
{
	string s1("hello world");
	string s2;
	if (s1.empty())
		cout << "s1 is empty" << endl;
	if (s2.empty())
		cout << "s2 is empty" << endl;
	return 0;
}

输出:s2 is empty

4.clear:清空有效字符

void clear();
int main()
{
	string s("hello world");
	cout << s.size() << endl;// 11
	cout << s.length() << endl;// 11
	cout << s.capacity() << endl;// 15
	cout << s << endl;// hello world

	s.clear();
	cout << s.size() << endl;// 0
	cout << s.capacity() << endl;// 15
	return 0;
}

该函数将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小

5.reserve:为字符串预留空间

void reserve (size_type n = 0);
int main()
{
	string s("hello world");
	cout << s.size() << endl;// 11
	cout << s.capacity() << endl;// 15

	s.reserve(100);
	cout << s.size() << endl;// 11
	cout << s.capacity() << endl;// 111

	s.reserve(50);
	cout << s.size() << endl;// 11
	cout << s.capacity() << endl;// 111
}

为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。

如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好,避免频繁扩容。

6.resize:将有效字符的个数该成n个,多出的空间用字符c填充

void resize (size_t n);
void resize (size_t n, char c);
int main()
{
	string s;//空字符串

	// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
	s.resize(10, 'a');
	cout << s.size() << endl; // 10
	cout << s.capacity() << endl;// 15
	cout << s << endl;	// “aaaaaaaaaa”

	// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
	// 注意此时s中有效字符个数已经增加到15个
	s.resize(15);
	cout << s.size() << endl;// 15
	cout << s.capacity() << endl;// 15
	cout << s << endl; // "aaaaaaaaaa\0\0\0\0\0"

	// 将s中有效字符个数缩小到5个
	s.resize(5);
	cout << s.size() << endl;// 5
	cout << s.capacity() << endl;// 15
	cout << s << endl; // "aaaaa"
	return 0;
}

resize(size_t n) resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用 来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。

注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

3.string类对象的访问及遍历操作

函数名称 功能说明
operator[] 返回pos位置的字符,const string类对象调用
begin+ end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的逆向迭代器
范围for C++11支持更简洁的范围for的新遍历方式

1.operator[]:返回pos位置的字符,const string类对象调用

char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
int main()
{
	string s1("hello world");
	const string s2("Hello world");
	cout << s1 << "   " << s2 << endl;//hello world   Hello world
	cout << s1[0] << " " << s2[0] << endl;//h H

	s1[0] = 'H';
	cout << s1 << endl;//Hello world
	// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改
}

2.string类对象的遍历操作

3种遍历方式:①for+operator[]   ②迭代器    ③范围for

iterator begin();
const_iterator begin() const;

iterator end();
const_iterator end() const;

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rend();
const_reverse_iterator rend() const;
int main()
{
	string s("hello world");
	// 3种遍历方式:
	// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
	// 另外以下三种方式对于string而言,第一种使用最多
	// 1. for+operator[]
	for (size_t i = 0; i < s.size(); ++i)
		cout << s[i];

    //hello world
	cout << endl;

	// 2.迭代器
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
    //hello world
	cout << endl;

	// string::reverse_iterator rit = s.rbegin();
	// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
	// 逆置打印
	auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		++rit;
	}
    //dlrow olleh
	cout << endl;

	// 3.范围for
	for (auto ch : s)
		cout << ch;
    //hello world
	return 0;
}

4.string类对象的修改操作

函数名称 功能说明
push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串
insert 在字符串pos位置插入一个字符或字符串
erase 删除字符串pos位置之后的len个字符
operator+= 在字符串后追加字符串str
c_str 返回C格式字符串
find  从字符串中pos位置从前向后找字符c,返回该字符在字符串中的位置
rfind 从字符串中pos位置从后向前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回

1.push_back:在字符串后尾插字符c

void push_back (char c);
string s1("hello");
s1.push_back('!');
cout << s1 << endl; //hello!

2.append:在字符串后追加一个字符串

string& append (const string& str);//追加str
string& append (const string& str, size_t subpos, size_t sublen);
//追加str从subpos位置开始的向后sublen个字符
string& append (const char* s);//追加字符串s
string& append (const char* s, size_t n);//追加字符串s的n个字符
string& append (size_t n, char c);//追加n个c字符
string s1("hello");
string s2(" world");
s1.append(s2); //hello world
s1.append(s2, 1, 5);//hello worldworld
s1.append("!!!");//hello worldworld!!!
s1.append("12345", 3);//hello worldworld!!!123
s2.append(3, '!');// world!!!

3.insert:在字符串pos位置插入一个字符或字符串

string& insert (size_t pos, const string& str);
//在pos位置插入str
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
//在pos位置插入str的从subpos位置开始的sublen个字符
string& insert (size_t pos, const char* s);
//在pos位置插入字符串s
string& insert (size_t pos, const char* s, size_t n);
//在pos位置插入字符串s的n个字符
string& insert (size_t pos, size_t n, char c); 
//在pos位置插入n个字符c
string s1("012345");
string s2("xxabxxcd");
string s3("**");
s1.insert(1,s3);//0**12345
s1.insert(3, s2, 0,2);//0**xx12345
s3.insert(2,"11");//**11
s3.insert(4, "2234", 3);//**11223
s3.insert(1, 3, 'x'); //*xxx*11223

4.operator+=:在字符串后追加字符串str

string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
string s1("hello");
string s2("world");
s1 += s2;//helloworld
s1 += "123"; //helloworld123
s1 += '!'; //helloworld123!

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

5.c_str:返回C格式字符串

const char* c_str() const;
string s1("hello world");
const char* pstr = s1.c_str();
cout << pstr << endl;//hello world

返回一个指向数组的指针,该数组包含一个以null结尾的字符序列(即C字符串),表示字符串对象的当前值。

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

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

npos是string里面的一个静态成员变量

static const size_t npos = -1;为size_t类型的最大值,一般用于表示到字符串的末尾。

string s1("hello world");
string s2 = s1.substr();
cout << s2 << endl;//hello world

7.find,rfind:从字符串中pos位置从前(后)向后(前)找字符c,返回该字符在字符串中的位置。

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;

size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (char c, size_t pos = npos) const;
string s1("hello,world!!");
string s2("world");
cout << s1.find(s2) << endl;//6
cout << s1.find("world") << endl;//6
cout << s1.find('!') << endl;//11
cout << s1.rfind(s2) << endl;//6
cout << s1.rfind("world") << endl;//6
cout << s1.rfind('!') << endl;//12

5. string类非成员函数

函数 功能说明
operator+ 尽量少用,因为传值返回,导致深拷贝效率低
operator>> 输入运算符重载
operator<< 输出运算符重载
getline 获取一行字符串
relational operators 大小比较

string类的其他接口就不一一列举和介绍了,有需要查文档即可。

二、string的模拟实现

1.默认成员函数和成员变量

为了与原来的string类进行区分,我们在命名空间中模拟实现:

#include
#include
using namespace std;
namespace sss//命名空间
{
	class string
	{
	public:
		string(const char* str = "")//构造函数
		{}
		string(const string& str)//拷贝构造函数
		{}
        string& operator=(const string& s)//赋值运算符重载函数
        {}   
		~string()//析构函数
		{}
	private:
		char* _str;
		size_t _size;//有效字符个数
		size_t _capacity;//存储有效字符串的空间大小(不包含'\0')

		static const size_t npos;//表示字符串末尾
	};
	const size_t string::npos = -1;
}

1.构造函数

我们一般会用一个常量字符串来进行初始化,因此用const char* str的指针接收。

如果str为空字符串,即_size = 0时,多开辟几个字节的空间,防止后续按capacity倍数扩容时,出现capacity=0的情况。

string(const char* str = "")//构造函数
		:_size(strlen(str))
	{
		_capacity = _size == 0 ? 3 : _size;
		_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
		strcpy(_str, str);
	}

2.析构函数

释放开辟的空间,成员变量置空即可。

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

3.拷贝构造函数

浅拷贝与深拷贝

我们来调用下方的test1()函数。

using namespace std;
namespace sss
{
	class string
	{
	public:
		string(const char* str = "")//构造函数
			:_size(strlen(str))
		{
			_capacity = _size == 0 ? 3 : _size;
			_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
			strcpy(_str, str);
		}
		~string()//析构函数
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
	private:
		char* _str;
		size_t _size;//有效字符个数
		size_t _capacity;//存储有效字符串的空间大小(不包含'\0')

		static const size_t npos;//表示字符串末尾
	};
	const size_t string::npos = -1;

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

此处发生崩溃,上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。

C++:string类的常用接口说明及其模拟实现_第1张图片

调用默认的拷贝构造后,导致的问题是:s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

浅拷贝:

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

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

深拷贝:

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

先拷贝其大小和容量,再开辟一块新的空间,将其字符串拷贝过去。

string(const string& s)//拷贝构造函数
		:_size(s._size)
		, _capacity(s._capacity)
	{
		_str = new char[(_capacity + 1)];
		strcpy(_str, s._str);
	}

4.赋值运算符重载函数

新开辟一块和原字符串相等的空间,将字符串拷贝过去,在释放掉其原本指向的空间。

string& operator=(const string& s)//赋值运算符重载函数
	{
		if (this != &s)//防止出现自己给自己赋值的情况
		{
			char* tmp = new char[(s._capacity + 1)];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
		}
		return *this;
	}

2.迭代器相关函数的实现

string类中的迭代器可以把它成是一个char* 的指针。

typedef char* iterator;
typedef const char* const_iterator;

iterator begin();//返回第一个字符的位置
iterator end();//返回最后一个字符的下一个位置
const_iterator begin()const;
const_iterator end()const;
iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}

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

使用演示:

void test2()
{
	string s1("hello world");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		*it += 1;//const迭代器只能读,不能修改
		cout << *it;
		++it;
	}
	//输出结果:ifmmp!xpsme
}

3.字符串大小与容量相关函数的实现

1.大小和容量

size_t size()const//返回当前字符串的有效个数
{
	return _size;
}
size_t capacity()const//返回当前字符串存储空间的大小
{
	return _capacity;
}

bool empty()const//判断字符串是否为空
{
	return 0 == _size;
}

2.reserve 和 resize 函数

void reserve(size_t newCapacity)//预留空间
{
	// 如果新容量大于旧容量,则开辟空间
    if (newCapacity > _capacity)
    {
		char* str = new char[newCapacity + 1];
		strcpy(str, _str);

		// 释放原来旧空间,然后使用新空间
		delete[] _str;
		_str = str;
		_capacity = newCapacity;
    }
}


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

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

4.字符串修改操作相关函数的实现

1.push_back()函数

void push_back(char c)
{
	if (_size == _capacity)//判断是否需要扩容
		reserve(_capacity * 2);
        //调用reserve预留空间
	_str[_size++] = c;
	_str[_size] = '\0';
}

2.insert()函数

// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char ch)
{
	assert(pos <= _size);//确保pos合法
	if (_size + 1 > _capacity)
	{
		reserve(_capacity * 2);
	}

	size_t end = _size + 1;
	while (end > pos)//将pos位置之后的字符向后移动
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	++_size;
	return *this;
}

string& insert(size_t pos, const char* str)//方法同上
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_size + len > _capacity)
		reserve(_size + len);
	size_t end = _size + len;
	while (end > pos + len - 1)//将pos位置之后的字符向后移动
	{
		_str[end] = _str[end - len];
		--end;
	}
	strncpy(_str + pos, str, len);
	_size += len;
	return *this;
}

3.erase()函数

第一种情况删除pos位置后的全部字符,直接将pos位置改为'\0',将_size赋值为pos即可。(pos为字符串的下标)

第二种情况删除pos位置后的部分字符串,将待删除字符串后面的字符串向前移动即可。_size减去删除的长度len。

string& erase(size_t pos, size_t len = npos)
{
    //npos表示字符串结束位置
	assert(pos <= _size);
	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
	return *this;
}

4.append函数和 operator+=

append函数直接复用insert即可,即在字符串末尾追加一个字符串。

void append(const char* str)
{
	insert(_size, str);
}

operator+=直接复用push_back 和 appen即可,即分布追加一个字符和字符串。

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

5.clear函数

清空字符串,直接将有效字符个数_size置为0,再将第一个字符置为'\0',将字符串置为空串。

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

6.swap函数

直接调用标准库里的swap函数,分布交换其成员变量即可。

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

7.c_str函数

返回c类型的字符串。

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

5.字符串访问相关函数的实现

1.operator[]

返回字符串中下标为pos位置的元素。

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

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

2.find()函数

size_t find(char ch, size_t pos = 0)
{
	assert(pos <= _size);//确保pos合法
	for (size_t i = pos; i < _size; ++i)
	{
		if (_str[i] == ch)
			return i;
	}
	return npos;
}

size_t find(const char* str, size_t pos = 0)
{
	assert(pos <= _size);
	char* p = strstr(_str + pos, str);//调用库函数
	if (p == nullptr)
		return npos;
	else
		return p - _str;//得到的元素个数,即为目标字符串开头的下标
}

6.字符串比较相关函数的实现

字符串比较调用标准库的strcmp函数,其余对已经实现的运算符重载进行复用。

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

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

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

7.流插入和流提取的实现

流插入:逐个字符输出。

ostream& operator<<(ostream& _cout, const sss::string& s)
{
    // 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
    //cout << s._str;
    for (size_t i = 0; i < s.size(); ++i)
    {
	    _cout << s[i];
    }
    return _cout;
}

流提取:

istream& operator>>(istream& in, string& s)
{
	s.clear();//清空字符串
	char ch = in.get();//读取一个字符
	char buff[128];//用做缓存数组,避免频繁开辟空间
	size_t i = 0;
	while (ch != ' ' && ch != '\n')//这两个符号为分割符
	{
		buff[i++] = ch;
		if (i == 127)//当数组满了,将字符串写入目标串
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}
	}
	if (i != 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

8.完整代码

#include
#include
using namespace std;
namespace sss
{
	class string
	{
	public:
		string(const char* str = "")//构造函数
			:_size(strlen(str))
		{
			_capacity = _size == 0 ? 3 : _size;
			_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
			strcpy(_str, str);
		}
		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;
		}
		~string()//析构函数
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 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;
		}

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

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

		void reserve(size_t newCapacity)
		{
			// 如果新容量大于旧容量,则开辟空间
			if (newCapacity > _capacity)
			{
				char* str = new char[newCapacity + 1];
				strcpy(str, _str);

				// 释放原来旧空间,然后使用新空间
				delete[] _str;
				_str = str;
				_capacity = newCapacity;
			}
		}

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

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

		void push_back(char c)
		{
			if (_size == _capacity)
				reserve(_capacity * 2);

			_str[_size++] = c;
			_str[_size] = '\0';
		}
		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size + 1 > _capacity)
			{
				reserve(_capacity * 2);
			}

			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			++_size;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
				reserve(_size + len);
			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}

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

		void append(const char* str)
		{
			insert(_size, str);
		}

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

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

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

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

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

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

		size_t find(char ch, size_t pos = 0)
		{
			assert(pos <= _size);
			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos <= _size);
			char* p = strstr(_str + pos, str);
			if (p == nullptr)
				return npos;
			else
				return p - _str;
		}

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

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

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

	private:
		friend ostream& operator<<(ostream& _cout, const sss::string& s);
		friend istream& operator>>(istream& _cin, sss::string& s);
	private:
		char* _str;
		size_t _size;//有效字符个数
		size_t _capacity;//存储有效字符串的空间大小(不包含'\0')

		static const size_t npos;//表示字符串末尾
	};
	const size_t string::npos = -1;

	ostream& operator<<(ostream& _cout, const sss::string& s)
	{
		// 不能使用这个, 因为string的字符串内部可能会包含\0
		// 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
		//cout << s._str;
		for (size_t i = 0; i < s.size(); ++i)
		{
			_cout << s[i];
		}
		return _cout;
	}
	istream& operator>>(istream& in, string& s)
    {
	    s.clear();//清空字符串
	    char ch = in.get();//读取一个字符
	    char buff[128];//用做缓存数组,避免频繁开辟空间
	    size_t i = 0;
	    while (ch != ' ' && ch != '\n')//这两个符号为分割符
	    {
		    buff[i++] = ch;
		    if (i == 127)//当数组满了,将字符串写入目标串
		    {
			    buff[127] = '\0';
			    s += buff;
			    i = 0;
		    }
	    }
	    if (i != 0)
	    {
		    buff[i] = '\0';
		    s += buff;
	    }
	    return in;
    }
}

你可能感兴趣的:(c++,开发语言)