【C++游记】string的使用和模拟实现

枫の个人主页

你不能改变过去,但你可以改变未来

算法/C++/数据结构/C

Hello,这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕,我们继续来学习C++的内容呀。C++是接近底层有比较经典的语言,因此学习起来注定枯燥无味,西游记大家都看过吧~,我希望能带着大家一起跨过九九八十一难,降伏各类难题,学会C++,我会尽我所能,以通俗易懂、幽默风趣的方式带给大家形象生动的知识,也希望大家遇到困难不退缩,遇到难题不放弃,学习师徒四人的精神!!!故此得名【C++游记】
话不多说,让我们一起进入今天的学习吧~~~

目录

1>>标准库的String

1.1>>auto和范围for小知识

1.2>>string常用接口(组件)这里都可以查找到

容量接口

访问和遍历

string类对象的修改操作

非成员函数但会用到

2>>string的模拟实现

3>>Boos战------string重要接口的模拟实现

1.String.h

2.String.cpp

3.wctest.cpp

4>>结语


1>>标准库的String

1.1>>auto和范围for小知识

师傅别怕,赠与"武器"一把i

auto基本知识:

它声明引用类型时,必须在auto后加上&。

声明多个变量时,这些变量必须是相同的类型。

auto不能作为函数参数,可以作为返回值。

不能用于声明数组。

cpp 复制代码
#include<iostream>
using namespace std;
int main() {
	auto a = 10;
	auto b = 'b';
	auto c = "asdofih";
	auto d = 11.11;
	auto& e = a;
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << d << endl;
	cout << e << endl;
	printf("%p %p", &a, &e);
	return 0;
}

范围for:

对比C语言的for循环,C++使用更好用的范围for代替, 格式为:"for(auto 范围内用于迭代的变量 : 被迭代的范围)",自动迭代、取数据、结束,这就是自动范围for。

cpp 复制代码
#include<iostream>
using namespace std;
int main() {
	int arr[] = { 1,2,3,4,5 };
	for (auto& e : arr) {//若要更改里面的数值,那么就要用引用才能修改到数组本身
		e += 2;
	}
	for (auto e : arr) {
		cout << e << " ";
	}
	cout << endl;
	string str("hello world");
	for (auto ch : str) {
		cout << ch << " ";
	}
	cout << endl;
	return 0;
}

1.2>>string常用接口(组件)这里都可以查找到

师傅后面都会介绍,不必担心

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

cpp 复制代码
void Test1() {
	string s1;//构造空的string类对象
	string s2("hello feng");//用常量字符串构造string类对象
	string s3(s2);//用s2拷贝构造s3
}

容量接口

若定义为string s,以下表size为例,就是s.size();

|----------|------------------------------|
| size | 返回字符串有效字符长度,使用范围比length广 |
| length | 返回字符串有效字符长度 |
| capacity | 返回总空间大小 |
| empty | 检测字符串释放为空串,空返回true,非空返回false |
| clear | 清空有效字符 |
| reserve | 为字符串预留空间 |
| resize | 将有效字符个数设置为n个,多余用c填充 |

补充说明:size和length的区别只有在引入迭代器才能体现出来,一般都用size,length有使用限制。

clear只清空,不改大小

resize(size_t n,char c)用变量c的字符填充多余空间,resize(size_t)则是用0来填充。

reserve预留空间,小于string的空间大小时不作改动。

访问和遍历

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

string类对象的修改操作

|------------|---------------------------|
| 函数名称 | 功能说明 |
| push_back | 在字符串后尾插字符c(单个) |
| append | 在字符串后追加一个字符串(多个) |
| operator+= | 在字符串后追加一个字符串(多个) |
| c_str | 返回C格式字符串 |
| find+npos | 从字符串pos位置往后查找字符c,返回它的位置 |
| rfind | 从字符串pos位置往前查找字符c,返回它的位置 |
| substr | 在str中从pos位置开始,截取n个字符,将其返回 |

非成员函数但会用到

|----------------------|---------|
| 函数 | 功能说明 |
| operator+ | 深拷贝,效率低 |
| operator>> | 输入运算符重载 |
| operator<< | 输出运算符重载 |
| getline | 获取一行字符串 |
| relational operators | 大小比较 |

2>>string的模拟实现

错误示范:

cpp 复制代码
#include<iostream>
#include<assert.h>
using namespace std;
class MyString {
public:
	MyString(const char* str = "") {
		if (str == nullptr) {
			perror("flase");
			return;
		}
		_str = new char[strlen(str) + 1];//加1放\0
		strcpy(_str, str);
	}
	~MyString() {
		if (_str) {
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
void Test2() {
	string s1("hello feng");
	string s2(s1);
}

int main() {
	/*Test1();*/
	Test2();
	return 0;
}

上述过程会报错,因为没有写拷贝构造,s2会调用s1的默认构造,而在类和对象章节中我们知道默认构造都是浅拷贝,这里是一个字符数组,因此要调用深拷贝。

cpp 复制代码
#include<iostream>
#include<assert.h>
using namespace std;
class MyString {
public:
	MyString(const char* str = "") {//构造
		if (str == nullptr) {
			perror("flase");
			return;
		}
		_str = new char[strlen(str) + 1];//加1放\0
		strcpy(_str, str);
	}
	MyString(const MyString& s)//拷贝构造
		:_str(nullptr)
	{
		MyString tmp(s._str);
		swap(tmp._str, _str);
	}
	~MyString() {//析构
		if (_str) {
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
void Test2() {
	string s1("hello feng");
	string s2(s1);
}

int main() {
	/*Test1();*/
	Test2();
	return 0;
}

3>>Boos战------string重要接口的模拟实现

这里直接附上上个文件源码,大部分已经注释完毕,请大家伙享用,感兴趣可以复制过去测试,不过还是自己敲一遍比较好。

1.String.h

cpp 复制代码
#pragma once

#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;

namespace wc
{
	class string
	{
	public:
		typedef char* iterator;//手动自造迭代器,名字与库中相同,但在wc的命名空间里
		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;
		}

		void swap(string& s);//交换,直接套用标准库里的swap

		string(size_t n, char ch);//设置相同字符多少个为一个字符串
		
		string(const char* str = "");//构造函数

		string(const string& s);//拷贝构造函数
		
		~string();//析构函数
		void clear()//清除
		{
			_str[0] = '\0';
			_size = 0;
		}

		string& operator=(string s);//string& operator=(const string& s);

		const char* c_str() const//常数直接返回自己地址,比较快因此调用内联
		{
			return _str;
		}

		void reserve(size_t n);//保存,为字符串预留空间
		void push_back(char ch);//类似于尾插
		void append(const char* str);//尾插一个字符串
		string& operator+=(char ch);//类似于尾插
		string& operator+=(const char* str);//尾插一个字符串

		void insert(size_t pos, size_t n, char ch);//在pos位置插入n个ch字符
		void insert(size_t pos, const char* str);//在pos位置插入一个字符串
		void erase(size_t pos = 0, size_t len = npos);//在pos位置删除len个字符

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

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _size;
		}

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

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

		string substr(size_t pos, size_t len = npos);//从pos位置开始取len个作为子串返回

		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;
	private:
		char* _str = nullptr;//定义字符串数组_str
		size_t _size = 0;//有效个数
		size_t _capacity = 0;//总容量
		const static size_t npos;//设置默认结束值
	};

	// cout<<s1
	ostream& operator<<(ostream& out, const string& s);
	// cin>>s1
	istream& operator>>(istream& in, string& s);

	istream& getline(istream& is, string& s, char delim = '\n');

	void test_string1();
	void test_string2();
	void test_string3();
	void test_string4();
	void test_string5();
	void test_string6();
	void test_string7();
}

2.String.cpp

cpp 复制代码
//#include<iostream>
//using namespace std;
//int main() {
//	auto a = 10;
//	auto b = 'b';
//	auto c = "asdofih";
//	auto d = 11.11;
//	auto& e = a;
//	cout << a << endl;
//	cout << b << endl;
//	cout << c << endl;
//	cout << d << endl;
//	cout << e << endl;
//	printf("%p %p", &a, &e);
//	return 0;
//}
//#include<iostream>
//using namespace std;
//int main() {
//	int arr[] = { 1,2,3,4,5 };
//	for (auto& e : arr) {//若要更改里面的数值,那么就要用引用才能修改到数组本身
//		e += 2;
//	}
//	for (auto e : arr) {
//		cout << e << " ";
//	}
//	cout << endl;
//	string str("hello world");
//	for (auto ch : str) {
//		cout << ch << " ";
//	}
//	cout << endl;
//	return 0;
//}

//void Test1() {
//	string s1;//构造空的string类对象
//	string s2("hello feng");//用常量字符串构造string类对象
//	string s3(s2);//用s2拷贝构造s3
//}
//#include<iostream>
//#include<assert.h>
//using namespace std;
//class MyString {
//public:
//	MyString(const char* str = "") {//构造
//		if (str == nullptr) {
//			perror("flase");
//			return;
//		}
//		_str = new char[strlen(str) + 1];//加1放\0
//		strcpy(_str, str);
//	}
//	MyString(const MyString& s)//拷贝构造
//		:_str(nullptr)
//	{
//		MyString tmp(s._str);
//		swap(tmp._str, _str);
//	}
//	~MyString() {//析构
//		if (_str) {
//			delete[] _str;
//			_str = nullptr;
//		}
//	}
//private:
//	char* _str;
//};
//void Test2() {
//	string s1("hello feng");
//	string s2(s1);
//}
//
//int main() {
//	/*Test1();*/
//	Test2();
//	return 0;
//}

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"

namespace wc
{
	const size_t string::npos = -1;

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

	string::string(size_t n, char ch)
		:_str(new char[n + 1])
		, _size(n)
		,_capacity(n)
	{
		for (size_t i = 0; i < n; i++) {
			_str[i] = ch;
		}
		_str[_size] = '\0';
	}

	string::string(const char* str)//声明中写过默认值,这里就不能再写了 
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_size + 1];
		strcpy(_str, str);//复制还没复制\0
		_str[_size] = '\0';
	}

	string::string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}
	string& string::operator=(string s) 
	{
		swap(s);
		return *this;
	}

	void string::reserve(size_t n) {//预留空间
		if (n > _capacity) {//大于就扩容
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	void string::push_back(char ch) {
		if (_size + 1 > _capacity) {//大于就扩容
			reserve(_capacity == 0 ? 4 : _capacity * 2);//预留空间刚写就能用
		}
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';
	}
	void string::append(const char* str) {
		size_t len = strlen(str);
		if (_size + len > _capacity) {
			reserve(_size + len);
		}
		strcpy(_str + _size, str);
		_size += len;
	}

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

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

	void string::insert(size_t pos, size_t n, char ch)
	{
		assert(pos <= _size);
		assert(n > 0);

		if (_size + n > _capacity)
		{
			// 扩容
			size_t newCapacity = 2 * _capacity;
			if (_size + n > 2 * _capacity)
			{
				newCapacity = _size + n;
			}

			reserve(newCapacity);
		}

		size_t end = _size + n;
		while (end > pos + n - 1) {
			_str[end] = _str[end - n];
			end--;
		}
		for (size_t i = 0; i < n; i++) {
			_str[pos + i] = ch;
		}
		_size += n;
	}

	void string::insert(size_t pos, const char* str)
	{
		size_t n = strlen(str);
		insert(pos, n, 'x');//覆盖一下
		for (size_t i = 0; i < n; i++)
		{
			_str[pos + i] = str[i];
		}
	}

	void string::erase(size_t pos, size_t len) {
		if (len > _size - pos) {//多删了
			_str[pos] = '\0';
			_size = pos;
		}
		else {
			size_t end = pos + len;
			while (end <= _size) {//\0也拷贝
				_str[end - len] = _str[end];
				end++;
			}
			_size -= len;
		}
	}

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

		return npos;
	}

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

	string string::substr(size_t pos, size_t len)
	{
		if (len > _size - pos)//若超出,就等于剩下长度
			len = _size - pos;
		
		string tmp;
		tmp.reserve(len);
		for (size_t i = 0; i < len; i++) {
			tmp += _str[pos + i];//直接加等,简单易懂
		}
		return tmp;
	}

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

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

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

	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);
	}


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

		return out;
	}

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

		// 输入短串,不会浪费空间
		// 输入长串,避免不断扩容
		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();
		while (ch != ' ' && ch != '\n')//遇到空格和\n停止
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';//最后一个复制为0然后加上
				s += buff;
				i = 0;
			}
			//也可以直接写s+=ch,不过占用内存高
			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

	istream& getline(istream& in, string& s, char delim)
	{
		s.clear();

		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

	void test_string1()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		string s2;
		cout << s2.c_str() << endl;

		s1 += ' ';
		s1 += '+';
		s1 += "hello ";
		s1 += "hello feng1111111111111111111111111";
		cout << s1.c_str() << endl;
	}

	void test_string2()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		s1.insert(11, 3, 'x');
		cout << s1.c_str() << endl;

		s1.insert(6, 3, 'x');
		cout << s1.c_str() << endl;

		s1.insert(0, 3, 'x');
		cout << s1.c_str() << endl;

		string s2("hello world");
		cout << s2.c_str() << endl;

		s2.insert(11, "yyy");
		cout << s2.c_str() << endl;

		s2.insert(6, "yyy");
		cout << s2.c_str() << endl;

		s2.insert(0, "yyy");
		cout << s2.c_str() << endl;
	}

	void test_string3()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		s1.erase(6, 2);
		cout << s1.c_str() << endl;

		s1.erase(6, 20);
		cout << s1.c_str() << endl;

		s1.erase(3);
		cout << s1.c_str() << endl;

		string s2("hello helleoo");
		cout << s2.find('e') << endl;
		cout << s2.find("helle") << endl;
	}

	void test_string4()
	{
		string s1("hello world");
		for (size_t i = 0; i < s1.size(); i++)
		{
			s1[i]++;
			cout << s1[i] << " ";
		}
		cout << endl;

		string::iterator it = s1.begin();
		while (it != s1.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : s1)
		{
			cout << e << " ";
		}
		cout << endl;

		const string s2("hello world");
		for (auto e : s2)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_string5()
	{
		string s1("hello world");

		string sub1 = s1.substr(6, 3);
		cout << sub1.c_str() << endl;

		string sub2 = s1.substr(6, 300);
		cout << sub2.c_str() << endl;

		string sub3 = s1.substr(6);
		cout << sub3.c_str() << endl;

		string s2("hello fengxxxxxxxxxxxxxxxxxx");
		s1 = s2;
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;

		s1 = s1;
		cout << s1.c_str() << endl;
	}

	void test_string6()
	{
		/*string s1("hello world");
		string s2("hello bit");

		cout << s1 << endl;
		cout << s2 << endl;

		string s3;
		cin >> s3;
		cout << s3 << endl;*/

		string s1, s2;
		cin >> s1 >> s2;
		cout << s1 << endl;
		cout << s2 << endl;

		string s3;
		//getline(cin, s3);
		getline(cin, s3, '!');
		cout << s3 << endl;
	}

	void test_string7()
	{
		string s1("1111111111111");
		string s2(s1);
		cout << s1 << endl;
		cout << s2 << endl;

		string s3("222222222222222222222222222");
		s1 = s3;
		cout << s1 << endl;
		cout << s3 << endl;

		//s1.swap(s2);
		//swap(s1, s2);
		cout << s1 << endl;
		cout << s2 << endl;
	}
}

3.wctest.cpp

cpp 复制代码
#include"string.h"

int main()
{
	//wc::test_string1();
	//wc::test_string3();
	wc::test_string5();

	return 0;
}

4>>结语

今日C++到这里就结束啦,如果觉得文章还不错的话,可以三连支持一下。感兴趣的宝子们欢迎持续订阅小枫,小枫在这里谢谢宝子们啦~小枫の主页还有更多生动有趣的文章,欢迎宝子们去点评鸭~C++的学习很陡,时而巨难时而巨简单,希望宝子们和小枫一起坚持下去~你们的三连就是小枫的动力,感谢支持~

相关推荐
Amo 67297 分钟前
取消网络请求
开发语言·前端·javascript
未来之窗软件服务11 分钟前
软件架构设计——通用表单UI—未来之窗行业应用跨平台架构
开发语言·javascript·ui
落下闳12 分钟前
数据结构——单向链表实现及循环链表
c语言·数据结构·vscode·链表
午言若13 分钟前
C++ 中多态性在实际项目中的应用场景
c++
编程零零七13 分钟前
【Python】tensorflow中的argmax()函数
开发语言·python·信息可视化·数据分析·arm·python学习·python数据可视化
嘉琪00117 分钟前
js相关面试题
java·开发语言
robin_suli17 分钟前
动态规划子序列问题系列一>最长递增子序列的个数
数据结构·算法·动态规划
iiiiiankor21 分钟前
【C语言实现:用队列模拟栈与用栈模拟队列(LeetCode 225 & 232)】
c语言·开发语言·leetcode··队列
赏你个麻辣烫儿23 分钟前
日拱一卒(16)——leetcode学习记录:山脉数组峰值索引
学习·算法·leetcode
徐霞客32023 分钟前
C语言实现八大排序算法
数据结构·c++·算法·排序算法