【C++】string学习 — 手搓string类项目

news2024/11/22 18:33:08

在这里插入图片描述

手搓string项目

  • 1 string类介绍
  • 2 功能描述
  • 3 代码实现
    • 3.0 基础框架
    • 3.1 构造函数 和 析构函数
    • 3.2 流操作符重载 和 尾插扩容
    • 3.4 运算符重载
    • 3.5 实用功能
    • 3.6 迭代器模拟
  • 总结
  • 这里提供一下源代码:
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 string类介绍

C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。

在 C++ 的早期版本中,字符串处理并不是一个简单的事情。在 C++ 的最初版本中,字符串被处理为 char* 类型的指针,这使得字符串处理变得非常复杂,容易出错。例如,简单的字符串连接操作都需要手动管理内存,这无疑增加了编程的难度。
为了解决这个问题,C++98 引入了 头文件,其中包含了 string 类。这个类的引入,可以说是一场革命,因为它提供了一个安全、方便、可移植的字符串处理方式。也为以后STL的出现埋下了伏笔…

在现实生活中,string也有着大量的应用:

  1. 社交媒体: 当你在社交媒体上发布状态或评论时,你输入的文字内容会存储在一个 string 变量中。例如,你可能会写一条消息 like “I had a great day at the park!”,这条消息就是存储在一个 string 变量中的。
  2. 电子邮件: 当你写一封电子邮件时,正文内容、主题行和收件人地址等都可能是 string 类型的。例如,你可能会写一封主题为 “Meeting Invitation” 的邮件,内容为 “Dear John, please join us for a meeting at 10am tomorrow.”,这些内容都是以 string 形式存储的。
  3. 购物车: 在在线购物时,你的购物车中商品的名称、价格和数量等信息通常会存储在 string 类型的变量中。例如,你的购物车中可能有 “T-shirt”、" Jeans" 和 “Shoes” 等商品,这些商品名称都是以 string 形式存储的。
  4. 等等等…

这里我们不管他的底层:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
我们通过类与对象的相关知识来尝试完成 string项目!我们将通过先描述,在落地的原则开始,只有明白了功能模块,才能流畅的写出string。

2 功能描述

首先我们必须明白我们需要什么功能,所以我们可以熟悉一下官方的常用接口:string使用手册
当然,从实际出发不失为一种更好的选择,想象一下使用场景:

  1. 第一,因为本质是字符串,所以我们需要成员函数 char* _str,并且要做到很好控制的话还需要 数据大小size_t _size 和容量 size_t capacity。
  2. 第二,构造函数,析构函数必须要有的,而且构造函数需要支持多种构造方法(常量字符串,拷贝构造,空类构造)。
  3. 第三,我们一定要支持输入字符串来构造string类和输出string,这就需要做到<< >>的重载了。
  4. 第亖,要想实现输入>>的重载,就要辅助实现push_back尾插函数,实现了尾插那 +=的操作重载也就完成了。
  5. 第五,我们还需要通过对比大小的一系列操作符(== <= >= > < !=)的重载。
  6. 第六,根据字符串输入的特性,为了我们可以从一行中正确读取数据,我们还需getline函数来实现功能。
  7. 第七,回到最基础的功能增删查改,所以我们可以增加指定位置插入,指定位置删除,查找字符串等功能。
  8. 第八,对于C++新增特性迭代器,我们也可以用指针模拟实现一下。
  9. 第九,既然支持了迭代器,那最原始的小标操作也要支持一下。

以上就是对一个字符串类可能需要的功能的全面总结,通过实现这些功能,我们可以创建出一个既实用又灵活的字符串操作工具
接下来,我们将根据之前列出的功能需求,逐步实施我们的字符串模拟项目。在编写代码的过程中,我们必须保持细心和谨慎,这样可以避免后期出现不必要的调试困扰。

3 代码实现

在实现这个项目的过程中,我们需要注意以下几点:

  1. 保持代码的清晰和可读性(重中之重):在编写代码时,要注意命名规范、代码结构和注释,使得其他人能够轻松理解我们的代码。

  2. 模块化设计:将代码分为多个模块,每个模块负责一个特定的功能。这样可以降低代码的复杂度,也便于后期的维护和扩展。

  3. 充分测试一定一定!!!):在代码实现完成后,要进行充分的测试,确保每个功能的正确性和稳定性。我们可以使用单元测试和集成测试来验证代码的质量。

  4. 优化性能:在保证功能实现的基础上,尽量优化代码的性能。我们可以关注一些常见的性能瓶颈,如内存分配、字符串拼接等,并寻求优化的方法

总之,在实现这个项目的过程中,我们要注重代码的质量、可读性和可维护性。只有这样,我们才能构建出一个高效、稳定且易于扩展的字符串模拟类。接下来,让我们开始编写代码吧!

3.0 基础框架

我们先根据功能写一下大概的功能框架,方便书写:(其中许多函数需要重载多个)

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;



namespace bit {

	class string {

	public:
	//默认结尾
		static const int npos;
	//构造函数
		string() :
		{
		}
		string(const char* str = "")	
		{
		}
		string(const string& s = "") 
		{
		}
	//析构函数
		~string() {
		}
	//取等操作
		string& operator=(string s) {
		}

		//迭代器模拟
		char* begin() {
		}
		char* end() {
		}
		//逆转迭代器
		char* rbegin() {
		}
		char* rend() {
		}
		
		//交换
		void swap(string& s) {
		}
	

		//从pos位置开始搜索寻找ch第一次出现的位置
		size_t find(const char ch, size_t pos = 0) {
		}
		//从pos位置开始搜索寻找 字符串s 第一次出现的位置
		size_t find(const char* s, size_t pos = 0) {
		}
		//在pos位置插入字符
		void insert(const char ch, size_t pos = 0) {
		}
		//在pos位置插入字符串
		void insert(const char* s, size_t pos = 0) {
		}

		//返回成员变量
		size_t size() const{
		}
		size_t capacity() const {
		}
		//扩容操作
		void reserve(size_t n){
		}
		//重置数据大小
		void resize(size_t n , char ch = '\0') {
		}
		//在pos位置后消除 n 个元素
		void erase(size_t pos, size_t n = npos) {
		}
		//尾插
		void push_back(const char* s) {
		}
		void push_back(char s) {
		}
		//清空
		void clear() {
		}
		//+=操作符重载
		void operator+= (const char* s) {
		}
		void operator+= (char s) {
		}
	//运算符重载
	friend ostream& operator<< (ostream& out, const string& str);
	bool operator==(const string& s) {
	}
	bool operator>(const string& s) {
	}
	bool operator>=(const string& s) {
	}
	bool operator<=(const string& s) {
	}
	bool operator<(const string& s) {
	}
	bool operator!=(const string& s) {
	}
//私有成员函数
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

	//流操作符重载
	ostream& operator<< (ostream& out, const bit::string& str) {
	}
	istream& operator>> (istream& in,  bit::string& str) {
	}
	//获取一行
	istream& getline(istream& in, string& s) {
	}
	//结尾赋值
	const int string::npos = -1;

}


框架写好,我们就可以开始逐个实现,一定注意其中的逻辑,不要刻意去实现一个功能,要联系其他功能,看看是否存在联系,进而通过调用函数简便我们的实现过程。

3.1 构造函数 和 析构函数

构造函数我们使用全却省,拷贝构造2个:这里注意初始化列表的使用
因为涉及了指针操作,所以必要的初始化是十分需要的
全缺省构造函数十分好用


//常量字符串构造
string(const char* str = "")
	:_str(new char[strlen(str) + 1]),
	_size(strlen(str)),
	_capacity(strlen(str) + 1)
{
//调用函数简单完成
	strcpy(_str, str);
}
//拷贝构造
//这里使用到了 = 重载,所以它测试可以等到实现操作符重载之后在实现。
string(const string& s = "") :
	_str(new char[s._capacity + 1])
	,_size(0)
	,_capacity(0)
{
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}


析构函数就简单的多:正常释放空间即可


		~string() {
			delete[] _str;

			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}

再来增加一些获取私有变量的函数:

//返回成员变量
	size_t size() const{
		return _size;
	}
	size_t capacity() const{
		return _capacity;
	}

3.2 流操作符重载 和 尾插扩容

接下来我们实现一下流操作符,方便我们可以快速进行一下测试。
对于流操作我们应该写在全局,这就可以正常的传入参数,不然就会报错哦。

//简单打印即可,注意设置友元哦
ostream& operator<< (ostream& out, const bit::string& str) {
	out << str._str;
	return out;
}
//这里是优化版本,可以避免频繁开空间,优化性能
istream& operator>> (istream& in,  bit::string& str) {
	str.clear();
	char* buff = new char[128];
	char ch;
	ch = in.get();

	int count = 0;
	//先存入中间数组再存入string中
	while (ch != '\n' && ch != ' ') {

		buff[count++] = ch;
			ch = in.get();
		if (count >= 127) {
			buff[127] = '\0';
			str.push_back(buff);
			count = 0;
		}
	}

	buff[count] = '\0';
	str.push_back(buff);

	return in;
}

这里我们发现我们需要实现一下尾插操作才好进行流输入操作。看,这样一步一步我们就可以完成所需功能。
尾插 push_back

	//插入字符串
void push_back(const char* s) {
		//先扩容!!!
	while (_size + strlen(s) >= _capacity) {
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}
		//然后依次读入即可
	for (size_t i = 0; i < strlen(s); i++) {
		_str[_size++] = s[i];
	}
	_str[_size] = '\0';

}
		//插入单个字符
void push_back(char s) {
	while (_size + 1 >= _capacity) {
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}
	_str[_size++] = s;
	_str[_size] = '\0';
}

可以想到,要实现尾插,扩容是必不可少的!!
再补充一个更改数据大小的函数。
扩容 reserve

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		//重置数据大小
		void resize(size_t n , char ch = '\0') {
			
			if ( n <= _size ) {
				_str[n] = '\0';
				_size = n;

			}
			else {
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}

		}

这样我们 就初步完成了局部的可执行程序。接下来我们进行第一次测试来看是否能够成功运行:

void test_string1(){
	bit::string s1("123 456");
	bit::string s2("");
	cout << "\n-------流输出测试--------\n";
	cout << s1 << endl;
	cout << s1.size() << ' ' << s1.capacity();
	s1.push_back("abcdefg");
	cout << "\n-------尾插测试--------\n";
	cout << s1 << endl;
	cout << s1.size() << ' ' << s1.capacity();
	cout << "\n-------流输入测试--------\n";
	cin >> s2;
	cout << s2 << endl;
	cout << s2.size() << ' ' << s2.capacity();
}

来看效果:
在这里插入图片描述

这样我们初步就完成了我们基础功能。接下来我们继续实现!

3.4 运算符重载

这部分比较简单:
实现两个之后,就可以来回使用完成六个函数的书写!这是运算逻辑的体现,让我们的代码变得简洁明朗,减少冗杂,增加代码的可读性。


	bool operator==(const string& s) {
		if (strcmp(_str, s._str) == 0) return true;
		else return false;
	}
	bool operator>(const string& s) {
		if (strcmp(_str, s._str) > 0) return true;
		else return false;
	}
	bool operator>=(const string& s) {
		return _str == s._str || _str > s._str;
	}
	bool operator<=(const string& s) {
		return !(_str > s._str);
	}
	bool operator<(const string& s) {
		return !(_str >= s._str);
	}
	bool operator!=(const string& s) {
		return !(_str == s._str);
	}
	
string& operator=(string s) {
	//现代写法	
	//swap(tmp);

	char* tmp = new char[s._capacity + 1];
	strcpy(tmp, s._str);

	delete[] _str;

	_str = tmp;
	_size = s._size;
	_capacity = s._capacity;

	return *this;

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

然后我们来完成十分常用的+=操作,不用多说,这个底层逻辑和push_back是一致的,所以在底层直接调用即可,这样也变向保证了代码的鲁棒性,只需对一个功能做出维护,既可以扩展出其他接口。
注意这里面 的 = 重载,现代写法更加简单 只需一步 swap即可。这十分巧妙,通过调用不同函数就帮助我们改善了代码的复杂性。

//提供两个重载,让其使用体验更好
void operator+= (const char* s) {

		push_back(s);

}
void operator+= (char ch) {

		push_back(ch);

}

然后,再来测试一下,保证我们的功能可以正常使用,一定一定要测试哦!测试十分重要,千万不能忽视!!!小心驶得万年船!

void test_string2(){
	bit::string s1("123 456");
	bit::string s2("123");
	
	cout<< "-----------比较测试--------------\n" ;
	cout << (s2 == s1) << endl;
	cout << (s2 < s1) << endl;
	cout << (s2 > s1) << endl;
	cout << (s2 <= s1) << endl;
	cout << (s2 >= s1) << endl;
	cout << (s2 != s1) << endl;
	cout<< "-----------+=测试--------------\n" ;
	bit::string s3("123456789");
	s3 += "abc";
	char ch = '1';
	s3 += ch;
	cout << s3;

}

我们进行了每个操作符的测试和+= 单个字符 与字符串的测试。正常通过测试,返回的0 1 值符合我们的要求。
在这里插入图片描述

3.5 实用功能

上述我们已经实现了基本的功能,接下来我们要加入一些比较实用的功能,比如查找,指定位置插入,指定位置删除,获取一行的字符。这些函数大大加强了string 的可操作性,让string更加使用,与普通的 char 类型拉开差距!
注意我们都要提供两种重载,保证单个字符和字符串都可以正常进行操作

//指定位置删除
void erase(size_t pos, size_t n = npos) {
		assert(pos < _size);
		//n >= _size - pos 防止溢出!!!
		if (n == npos || n >= _size - pos) {
			_str[pos] = '\0';
			_size = pos;
		}
		else 
		{
			strcpy(_str + pos, _str + pos + n);
			_size -= n;
		}

}

//在pos位置插入字符串
	void insert(const char* s, size_t pos = 0) {
		assert(pos < _size);
		int len = strlen(s);
		//保证容量足够不然会发生报错哦
		while (_size + len >= _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		//挪动数据
		size_t end = _size + len;
		while (end > pos + len - 1 ) {
			_str[end] = _str[end - len];
			end--;
		}
		//拷贝到指定位置,不要拷贝‘\0’
		strncpy(_str + pos, s, len);

		_str[_size + strlen(s)] = '\0';
		_size += strlen(s);

	}
	//在pos位置插入字符
	void insert(const char ch, size_t pos = 0) {
		assert(pos < _size);

		while (_size +1 >= _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		//普通写法?!?!
		/*int 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;
	}
		//从pos位置开始搜索寻找ch第一次出现的位置
		size_t find(const 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;
		}

		//从pos位置开始搜索寻找 字符串s 第一次出现的位置
		size_t find(const char* s, size_t pos = 0) {
			assert(pos < _size);
			const char* p = strstr(_str, s);
			if (p) return p - _str;
			else return npos;
		}

写入了这么多功能,快来进行测试一波!

void test_string3() {
	bit::string s1("123 456 abc");
	cout << "---------find测试-------------\n";
	cout << s1.find('1') << endl;
	cout << s1.find("457") << endl;
	cout << "---------insert测试-------------\n";
	bit::string s2("123");
	s1.insert("jkl465798", 0);
	cout << s1 << endl;
	cout << "---------resize测试-------------\n";
	cout << s2 << endl;
	cout << s2.capacity() << endl;
	s1.resize(4);
	cout << s2 << endl;
	cout << s2.size() << ' ' << s2.capacity() << endl;
	s1.resize(10, 'c');
	cout << s2 << endl;
	cout << s2.size() << ' ' << s2.capacity() << endl;
	cout << "---------erase测试-------------\n";
	
	s2.erase(2, 5);
	cout << s2 << endl;
	cout << s2.size() << ' ' << s2.capacity() << endl;

}

在这里插入图片描述
我们成功完成了功能的扩展,实现了增删查改的重要部分,这下子我们的string就完成了绝大部分,接下来在补充上迭代器就更好了。

3.6 迭代器模拟

C++中的迭代器是用于访问容器元素的一种抽象指针。迭代器具有五个基本特性:

  1. 迭代器类型:迭代器是一个实现了迭代器协议的对象,它有一个类型,该类型指示它所指向的元素的类型。例如,在std::string中,迭代器类型是std::string::iterator
  2. 解引用:迭代器可以解引用,这意味着可以通过解引用迭代器来访问它所指向的元素。在std::string中,解引用迭代器可以访问字符串中的字符。
  3. 箭头操作符:大多数迭代器都支持箭头操作符->,用于访问迭代器所指向对象的成员。在std::string中,箭头操作符可以用于访问字符串中字符的成员函数,如std::string::iterator>std::string::value_type::operatorchar()
  4. 增加和减少:迭代器可以通过增加(++)和减少(–)操作符来遍历容器。在std::string中,增加迭代器会移动到下一个字符,减少迭代器会移动到前一个字符。
  5. 比较:迭代器可以比较,以确定它们是否指向同一个元素或是否在容器中相邻。在std::string中,两个迭代器可以通过比较操作符(==、!=)来比较它们是否相等,或者通过比较操作符(<、<=、>、>=)来比较它们的相对位置。

所以我们可以简单通过指针来模拟实现一下,让其可以初步使用即可。
依旧给出两套重载,保证常量与非常量的正常访问

		//迭代器模拟
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin() {
			return _str;
		}
		iterator end() {
			return _str + _size;
		}
		iterator rbegin() {
			return _str + _size;
		}
		iterator rend() {
			return _str;
		}
		const_iterator begin() const {
			return _str;
		}
		const_iterator end() const{
			return _str + _size;
		}
		const_iterator rbegin() const{
			return _str + _size;
		}
		const_iterator rend() const{
			return _str;
		}
		//提供下标访问 传回引用,可读可写
		char& operator[](size_t i) {
		//保证数组不越界!比普通数组越界更好,
		//普通数组是抽查 , 不够稳定!!!
			assert(i < _size);
			return _str[i];
		}
	//保证可以对常量string进行操作
		const char& operator[](size_t i) const {
			assert(i < _size);
			return _str[i];
		}

来进行测试一下:(const 变量与普通变量都进行测试)

void test_string4(){

	bit::string s1("123456789");
	for (auto ch : s1) {
		cout << ch << ' ';
	}

	reverse(s1.begin(), s1.end());
	cout << endl;

	for (size_t i = 0; i < s1.size();i++) {
		cout << s1[i] << " ";
	}
	cout << endl;
	const bit::string s2("abcdefg");
	for (auto ch : s2) {
		cout << ch << ' ';
	}

	cout << endl;

	for (size_t i = 0; i < s2.size();i++) {
		cout << s2[i] << " ";
	}

}

在这里插入图片描述

这下也间接证明了基于范围的for循环是以迭代器为底层的。并且我们实现了[ ] 的成功可读可写访问

总结

实现string类的过程就像是在黑暗中寻找光明,每一个难题都是我前进路上的绊脚石,但我没有退缩,我勇往直前。我看着那些曾经困扰着我的问题,一步步被我解决,就像是看着黑暗中的光明一点点被我点亮。那种成就感,那种喜悦,无法用言语表达!!!

肆无忌惮的放任自己,这样得来的自由,终将在现实中轰然倒塌。而自律赢来的,是你对现实的自主感,是真正的自由。

我看着电脑屏幕上的代码,每一个字符都像是我的朋友,我的伙伴。我用心去理解它们,去掌握它们,去运用它们。我发现,当我用自律的态度去面对这些代码时,它们不再是我眼中的难题,而是我手中的工具,是我实现梦想的桥梁。
我知道,这只是我开始,这只是我旅程的一小步。前方还有更多的挑战等待着我,有更多的困难需要我去克服。但我不再害怕,因为我知道,只要我保持着自律的态度,我就能够战胜一切

这里提供一下源代码:

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;



namespace bit {

	class string {

	public:
		static const int npos;
	//构造函数
		//string() :
		//_str(new char[1]),
		//_size(0),
		//_capacity(0)
		//{
		//	_str[0] = '\0';
		//}
		
		//一个全缺省即可
		string(const char* str = "")
			:_str(new char[strlen(str) + 1]),
			_size(strlen(str)),
			_capacity(strlen(str) + 1)
		{
			strcpy(_str, str);
		}

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

		~string() {
			delete[] _str;

			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}


		string& operator=(string s) {
			//现代写法	
			//swap(tmp);

			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);

			delete[] _str;

			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;

			return *this;

		}

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

		//迭代器模拟
		typedef char* iterator;
		typedef const char* const_iterator;

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

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

		//从pos位置开始搜索寻找ch第一次出现的位置
		size_t find(const 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;
		}

		//从pos位置开始搜索寻找 字符串s 第一次出现的位置
		size_t find(const char* s, size_t pos = 0) {
			assert(pos < _size);
			const char* p = strstr(_str, s);
			if (p) return p - _str;
			else return npos;
		}
		//在pos位置插入字符
		void insert(const char ch, size_t pos = 0) {
			assert(pos < _size);

			while (_size +1 >= _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			size_t end = _size + 1;

			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;


		}
		//在pos位置插入字符串
		void insert(const char* s, size_t pos = 0) {
			assert(pos < _size);
			int len = strlen(s);

			while (_size + len >= _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			
			size_t end = _size + len;

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

			strncpy(_str + pos, s, len);

			_str[_size + strlen(s)] = '\0';
			_size += strlen(s);

		}

		//返回成员变量
		size_t size() const{
			return _size;
		}
		size_t capacity() const{
			return _capacity;
		}
		//扩容操作
		
		//错误示范
		//void reserve(size_t newcapacity)
		//{
		//	char* tmp = new char[newcapacity + 1];
		//	strcpy(tmp, _str);
		//	_capacity = newcapacity + 1;
		//	delete _str;
		//	_str = tmp;
		//}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}
		//重置数据大小
		void resize(size_t n , char ch = '\0') {
			
			if ( n <= _size ) {
				_str[n] = '\0';
				_size = n;

			}
			else {
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}

		}
		//在pos位置后消除 n 个元素
		void erase(size_t pos, size_t n = npos) {
			assert(pos < _size);
			//防止溢出!!!
			if (n == npos || n >= _size - pos) {
				_str[pos] = '\0';
				_size = pos;
			}
			else 
			{
				strcpy(_str + pos, _str + pos + n);
				_size -= n;
			}

		}

		void push_back(const char* s) {
			while (_size + strlen(s) >= _capacity) {
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}

			for (size_t i = 0; i < strlen(s); i++) {
				_str[_size++] = s[i];
			}
			_str[_size] = '\0';

		}
		
		void push_back(char s) {
			while (_size + 1 >= _capacity) {
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size++] = s;
			_str[_size] = '\0';

		}
		//清空
		void clear() {
			_str[0] = '\0';
			_size = 0;
			_capacity = 0;
		}

		void operator+= (const char* s) {

			push_back(s);

		}
		void operator+= (char s) {

			push_back(s);

		}
	//运算符重载
	friend ostream& operator<< (ostream& out, const string& str);

	bool operator==(const string& s) {
		if (strcmp(_str, s._str) == 0) return true;
		else return false;
	}

	bool operator>(const string& s) {
		if (strcmp(_str, s._str) > 0) return true;
		else return false;
	}
	bool operator>=(const string& s) {
		return _str == s._str || _str > s._str;
	}
	bool operator<=(const string& s) {
		return !(_str > s._str);
	}
	bool operator<(const string& s) {
		return !(_str >= s._str);
	}
	bool operator!=(const string& s) {
		return !(_str == s._str);
	}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

	//运算符重载
	ostream& operator<< (ostream& out, const bit::string& str) {
		out << str._str;
		return out;
	}

	istream& operator>> (istream& in,  bit::string& str) {
		str.clear();
		char* buff = new char[128];
		char ch;
		ch = in.get();

		int count = 0;

		while (ch != '\n' && ch != ' ') {

			buff[count++] = ch;
 			ch = in.get();
			if (count >= 127) {
				buff[127] = '\0';
				str.push_back(buff);
				count = 0;
			}
		}

		buff[count] = '\0';
		str.push_back(buff);

		return in;
	}

	istream& getline(istream& in, string& s) {
		char ch;
		ch = in.get();

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

	const int string::npos = -1;

}

Thanks♪(・ω・)ノ谢谢阅读!!!

谢谢Thanks♪(・ω・)ノ!

下一篇文章见!!!

期待与你一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

摄像机内存卡删除的视频如何恢复?恢复指南来袭

在现代社会&#xff0c;摄像机已成为记录生活、工作和学习的重要设备。然而&#xff0c;随着使用频率的增加&#xff0c;误删或意外丢失视频的情况也时有发生。面对这样的情况&#xff0c;许多用户可能会感到无助和困惑。那么&#xff0c;摄像机内存卡删除的视频真的无法恢复吗…

【AnaConda/MiniConda/Linux】使用sudo python或切换root管理员conda环境被绕过解决方案

写在前面 部分机型修改环境变量存在风险&#xff0c;可能用于被覆盖而出现大量命令无法找到的情况 可以输入这个解决 export PATH/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin往期相关内容 探索Miniconda3&#xff1a;简单、灵活的Python环境和…

HTML 学习笔记(九)颜色值和长度单位

一、颜色 1.通过RGB值来设置颜色 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>table</title&…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的癌症图像检测系统(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;本篇博客深入介绍了如何借助深度学习技术开发癌症图像检测系统&#xff0c;以提高医疗诊断的精度和速度。系统基于先进的YOLOv8算法&#xff0c;并对比分析了YOLOv7、YOLOv6、YOLOv5的性能&#xff0c;如mAP和F1 Score。详细解释了YOLOv8的原理&#xff0c;并附…

【how2j练习题】css部分课堂练习

1.表格斑马线 <style>table {width: 500px;border-collapse: collapse;}tr#title {background-color: white;text-align: center;border-bottom: 5px solid gold;}tr#id1 {text-align: center;border-bottom: 2px solid blueviolet;}tr#id2 {text-align: center;border-b…

【C++】STL(六) list容器

7. list容器7.1 简介7.2 构造函数例子 7.3 赋值和交换例子 7.4 大小操作例子 7.5 插入和删除例子 7.6 数据存取例子 7.7 反转和排序例子 7. list容器 7.1 简介 ① 功能&#xff1a;将数据进行链式存储。 ② 链表(list)是一种物理存储单元上非连续的存储结构&#xff0c;数据…

Python环境下一维时间序列的小波尺度谱和时间平均小波谱(基于Morlet小波)

小波分析是较好的非平稳信号分析方法之一&#xff0c;它通过伸缩和平移运算对信号进行多尺度细化分析&#xff0c;能够在不同的尺度上描述信号的局部特征&#xff0c;为微弱故障特征信号的检测提供了有效的工具。小波尺度谱可看作一个有恒定相对带宽的谱图&#xff0c;能够反映…

Linux fork函数详解

文章目录 1 基本介绍2 fork实例2.1 多个fork返回值2.2 C语言 fork与输出2.3 fork &#x1f4a3; 1 基本介绍 #include <sys/types.h> #include <unistd.h>pid_t fork(void)描述 fork用于创建一个子进程&#xff0c;它与父进程的唯一区别在于其PID和PPID&#xff0…

【Linux】Linux小结

LVS、Nginx、HAproxy的区别 LVS、Nginx和HAproxy都是常见的负载均衡器&#xff0c;用于将网络负载分散到多个服务器上&#xff0c;以提高系统的可用性和性能 功能不同&#xff1a; LVS是一个Linux内核模块&#xff0c;在网络层&#xff08;第四层&#xff09;运行的。 Nginx和…

Java错误:微服务报错Cannot execute request on any known serve

&#x1f414;问题内容 报Cannot execute request on any known server 这个错&#xff1a;连接Eureka服务端地址不对。 &#x1f414;解决方式 检查.yml文件或者.properties文件配置 下划线下划线后面的小写字母等同于去掉下划线大写下划线后面的字母&#xff08;驼峰原则&am…

一道题学会如何使用哈希表

给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&#xff1a;nums [1,2,3], …

【任务计划程序】打卡签到(自用)

文章目录 前言效果如下&#xff1a; 任务计划程序其他签到代码&#xff08;更新如下&#xff09; 前言 【github actionpython】完成定时任务并推送&#xff08;学会自制&#xff09;&#xff08;这里的github上这个glados签到不能用了&#xff0c;glados上的部分功能也变了&a…

学完Efficient c++ (39-40)

条款39&#xff1a;明智而审慎地使用private继承 private继承的特点&#xff1a; 如果类之间是private继承关系&#xff0c;那么编译器不会自动将一个派生类对象转换为一个基类对象。由private继承来的所有成员&#xff0c;在派生类中都会变为private属性&#xff0c;换句话说…

大白话ChatGPT技术

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《ChatGPT架构设计与应用案例实践》 —1— ChatGPT 架构设计剖析 ChatGPT 总体架构由三大部分构成&#xff1a;预训练&#xff08;Pre-training&#xff09;架构、微调&#xff08;Fine-tuning&#xff09;架…

sqllab第五关通关笔记

知识点&#xff1a; 报错注入函数语法&#xff08;详见第二关笔记&#xff09;报错注入打印位数最多32位对于大于32位的数据最好使用截取函数进行控制&#xff1b;以保证输出完整mysql表中的重点数据库 information_schema &#xff08;mysql 5.0以上&#xff09; schemata …

揭秘 Kubernetes Secret:安全存储敏感信息的秘密武器

Kubernetes Secret Secret 是 Kubernetes 中用于存储敏感信息的资源&#xff0c;例如密码、API 密钥和 SSH 密钥。Secret 可以被 Pod 和其他 Kubernetes 资源使用&#xff0c;而无需将敏感信息暴露在配置文件或环境变量中。 1. Secret 类型 Kubernetes 支持多种类型的 Secre…

智能家居涉及到的12个物联网传感器!

智能家居领域涉及到的物联网传感器种类繁多&#xff0c;下面列举一些常见的物联网传感器&#xff1a; 温度传感器&#xff1a;用于检测室内温度&#xff0c;可以实现智能温控和节能控制。湿度传感器&#xff1a;用于检测室内湿度&#xff0c;可以实现智能湿度控制和防潮功能。…

Python Excel 文本编辑库之xlsxwriter使用详解

概要 在现代数据处理和报表生成中,Excel 文件是一个非常常见的格式。Python XlsxWriter 库是一个强大的工具,可以帮助开发者轻松创建和编辑 Excel 文件,并且具有高度的灵活性和可定制性。本文将全面介绍 XlsxWriter 库的原理、功能、用法,并通过丰富的示例代码来展示其强大…

Linux 安装 Gitblit

1.下载Gitblit 官网地址&#xff1a;Gitblit&#xff0c;目前最新的是1.9.3 2.上传到服务器 ①在服务器上新建目录&#xff1a;/usr/local/gitblit ②将下载的文件上传到服务器&#xff1a;/usr/local/gitblit/gitblit-1.9.3.tar.gz ③解压文件&#xff1a; cd /usr/local…

如何考上东南大学计算机学院?

东南大学招生学院是计算机科学与工程学院、苏州联合研究生院&#xff0c;复试公平&#xff0c;不歧视双非考生&#xff0c;985院校中性价比较高&#xff0c;但近年热度在逐年上涨&#xff0c;需要警惕。 建议报考计算机科学与工程学院081200计算机科学与技术专业目标分数为380…