【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++的学习很陡,时而巨难时而巨简单,希望宝子们和小枫一起坚持下去~你们的三连就是小枫的动力,感谢支持~

相关推荐
猷咪7 分钟前
C++基础
开发语言·c++
IT·小灰灰8 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧10 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q11 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳011 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾11 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB14 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683615 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
飞机和胖和黄27 分钟前
考研之王道C语言第三周
c语言·数据结构·考研
yyy(十一月限定版)28 分钟前
寒假集训4——二分排序
算法