当前位置: 首页 > news >正文

string的模拟实现

string.h

#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace yjc
{
	class string
	{
	public:
		//typedef char* iterator; // 实现迭代器, 原来迭代器就是一个像指针的东西,任何容器都是
		using iterator = char*; // 因为string底层的物理结构是连续的,所以直接用指针就可以实现
		using const_iterator = const char*;
		//string();
		//string(const char* str);
		string(const char* str = ""); 
		string(const string& s);
		string& operator= (const string& s);
		~string();

		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, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len = npos);

		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		string substr(size_t pos, size_t len = npos);
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		char& operator[](size_t i) // 可读可写
		{
			assert(i < _size); 
			return _str[i];
		}
		const char& operator[](size_t i) const
		{
			assert(i < _size);
			return _str[i];
		}

		size_t size() const
		{
			return _size;
		}

		iterator begin() // 迭代器要结合begin和end一起使用
		{
			return _str;
		}
		 
		iterator end()
		{
			return _str + _size; // 指向的是最后一个位置的下一个位置
		}

		const_iterator begin() const // 实现一个const修饰的迭代器 注意这里的命名,表示不是迭代器不能修改,而是迭代器指向的内容不能修改
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}
		const char* c_str() const// 隐含的this指针好用
		{
			return _str; 
		}

		void swap(string& s);
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		// static const size_t npos = -1; // 静态成员变量按理来说是类中声明类外定义,但是有const修饰 之后就可在类中定义,而且只有整形才可以, 是一个特殊处理
		static const size_t npos;
	};

	void swap(string& s1, string& s2);
	// 全部重载为全局函数,为了支持字符串和string比较,类中的成员函数有抢位置的问题
	bool operator== (const string& lhs, const string& rhs); // 常量字符串可以隐式类型转换为string类型
	bool operator!= (const string& lhs, const string& rhs);
	bool operator> (const string& lhs, const string& rhs);
	bool operator< (const string& lhs, const string& rhs);
	bool operator>= (const string& lhs, const string& rhs);
	bool operator<= (const string& lhs, const string& rhs);

	ostream& operator<<(ostream& os, const string& str);
	istream& operator>>(istream& is, string& str);
	istream& getline(istream& is, string& str, char delim = '\n');
}

string.cpp

#define  _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace yjc // 定义多个命名空间,名字相同的会默认合并
{
	const size_t string::npos = -1; // 走声明和定义分离
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1]; // 多开一个空间给\0
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0? 4: 2 * _capacity); // 使用三目表达式,因为有可能空间是0
		}
		_str[_size] = ch;
		_size++;
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		//size_t newCapacity = _capacity * 2;
		if (_capacity < _size + len)
			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, char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity); // 使用三目表达式,因为有可能空间是0
		}

		// 方法一
		//int end = _size;
		//while (end >= (int)pos) // 注意这里存在整形提升,会导致程序崩溃,所以必须强转int
		//{
		//	/*if (end == 0)
		//	{
		//		int i = 0;
		//	}*/ // 手动写一个条件断点,方便调试

		//	_str[end + 1] = _str[end];
		//	end--;
		//}

		// 方法二
		size_t end = _size + 1;
		while (end > _size)
		{
			_str[end] = _str[end - 1];
			end--;
		}
		_str[pos] = ch;
		_size++;
	}
	void string::insert(size_t pos, const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			size_t newCapacity = 2 * _capacity;
			if (_size + len > newCapacity)
			{
				newCapacity = _size + len;
			}
			reserve(newCapacity);
		}

		//int end = _size;
		//while (end >= (int)pos) // 防止整型提升
		//{
		//	_str[end + len] = _str[end];
		//	--end;
		//}

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

		for (int i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
	}
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t end = pos + len;
			while (end <= _size)
			{
				_str[end - len] = _str[end];
				++end;
			}
			_size -= len;
		}
	}

	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);
		for (int i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
				return i;
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, str); // 直接调用库函数,是一个暴力匹配的思想,返回第一个匹配的位置
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str; // 返回值是串中的n个元素
		}
	}
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < len);
		if (len > _size - pos)
		{
			len = _size - pos;
		}

		yjc::string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return sub; // 这边必须传值返回,调用拷贝构造
	}

	//string::string()
	//	:_str(new char[1] {'\0'}) // 这里注意不能直接给一个空指针,否则程序会崩溃,并且要用数组初始化,为了和析构保持一致
	//	, _size(0)
	//	, _capacity(0)
	//{}

	//string::string(const char* str)
	//	:_size(strlen(str))// 先开空间,再初始化,注意要+1 strlen不计算 \0 的空间
	//{
	//	// 只让_size走初始化列表,避免多次使用strlen,并且不用管定义的顺序
	//	_str = new char[_size + 1];
	//	_capacity = _size;
	//	strcpy(_str, str);
	//}

	string::string(const char* str)// 直接给一个全缺省的构造函数, 要指定类域
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}
	//传统写法
	//string::string(const string& s)
	//{
	//	_str = new char[s._capacity + 1]; // 完成深拷贝
	//	strcpy(_str, s._str);
	//	_size = s._size;
	//	_capacity = s._capacity;
	//}
	//现代写法
	string::string(const string& s)
	{
		string tmp(s._str); // 调用拷贝构造
		swap(tmp); // 复用swap
	}
	//string& string::operator= (const string& s)
	//{
	//	if (this != &s) // 防止自己给自己赋值
	//	{
	//		delete[] _str;
	//		_str = new char[s._capacity + 1];
	//		strcpy(_str, s._str);
	//		_size = s._size;
	//		_capacity = s._capacity;
	//		return *this; // 有连续赋值的情况,所以要有返回值
	//	}
	//}
	//现代写法
	string& string::operator= (const string& s)
	{
		if (this != &s)
		{
			string tmp(s._str);
			swap(tmp);
		}
		return *this;
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}
	void string::swap(string& s)
	{
		std::swap(_str, s._str); // 直接调用算法库的swap使其改变指针指向就行
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

	void swap(string& s1, string& s2)
	{
		s1.swap(s2); // 又实现一个全局swap为了实现和算法库的统一,这里的swap相当于是对string的swap的一个封装
		// 一共有三个swap,一个算法库的,一个string的,还有一个对string的封装的
	}

	bool operator== (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) == 0;
	}
	bool operator!= (const string& lhs, const string& rhs)
	{
		return !(lhs == rhs);
	}
	bool operator> (const string& lhs, const string& rhs)
	{
		return !(lhs <= rhs);
	}
	bool operator< (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) < 0;
	}
	bool operator>= (const string& lhs, const string& rhs)
	{
		return !(lhs < rhs);
	}
	bool operator<= (const string& lhs, const string& rhs)
	{
		return (lhs < rhs || lhs == rhs);
	}

	ostream& operator<<(ostream& os, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}
	istream& operator>>(istream& is, string& str)
	{
		str.clear();
		//方法一
		//char ch;
		is >> ch; // 流提取操作符的默认行为是忽略空格和换行
		//ch = is.get(); // get函数会读空格和换行 

		//方法二
		int i = 0;
		char buff[256]; // 在栈上开空间
		char ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			//str += ch;
			buff[i++] += ch;
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff; // 一次扩容
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}

	istream& getline(istream& is, string& str, char delim)
	{
		str.clear();
		int i = 0;
		char buff[256]; 
		char ch = is.get();
		while (ch != delim)
		{
			buff[i++] += ch;
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff; // 一次扩容
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}
}

test.cpp

#define  _CRT_SECURE_NO_WARNINGS 1
#include"string.h"

// 构造 遍历
void Test1()
{
	//yjc::string s1 = "hello world"; // 要加上命名空间,不然会默认先去库里面找
	//cout << s1.c_str() << endl;
	//s1[0] = 'x';
	//for (int i = 0; i < s1.size(); i++)
	//{
	//	cout << s1[i];
	//}

	yjc::string s2 = "hello world";
	cout << s2.c_str() << endl;
	yjc::string::iterator it = s2.begin();

	//while (it != s2.end())
	//{
	//	cout << *it;
	//	it++;
	//}

	while (it != s2.end())
	{
		(*it)--;
		cout << *it;
		it++;
	}

	cout << endl;

	for (auto& ch : s2) // 注意这边auto只能识别char,想用引用要自己加, 引用可以减少拷贝
	{
		ch++;
	}

	for (auto ch : s2) // 实现了迭代器 就能支持范围for,并且要咱找stl的命名规则命名迭代器
	{
		cout << ch;
	}

	cout << endl;
	const yjc::string s3 = "xxxxxxxx";
	for (auto& ch : s3)
	{
		// ch++;
		cout << ch;
	}
}

// 插入
void Test2()
{
	yjc::string s1 = "hello world";
	cout << s1.c_str() << endl;
	s1 += 'x';
	s1 += "6666";
	cout << s1.c_str() << endl;
	
	yjc::string s2 = "hello world";
	s2.insert(0, 'x');
	cout << s2.c_str() << endl;
	s2.insert(0, "yjcyjcyjcyjc");
	cout << s2.c_str() << endl;
}

void Test3()
{
	yjc::string s1 = "hello world";
	s1.erase(6, 2);
	cout << s1.c_str() << endl;
}

void Test4()
{
	yjc::string s1 = "hello world";
	cout << s1.find(' ') << endl;
	cout << s1.find("wor") << endl;
}

void Test5()
{
	yjc::string url = "https://zhrz.nuc.edu.cn/cas/login?service=https%3A%2F%2Fzhmh.nuc.edu.cn%2Fpersonal-center";
	size_t pos1 = url.find(':');
	size_t pos2 = url.find('/', pos1 + 3);
	if (pos1 != string::npos && pos2 != string::npos)
	{
		yjc::string domain = url.substr(pos1 + 3, pos2 - pos1 - 3); // 获取域名
		cout << domain.c_str() << endl;
	}
}

void Test6()
{
	yjc::string s1;
	//cin >> s1;
	//cout << s1;
	getline(cin, s1, '#');
	cout << s1;
}
int main()
{
	Test6();
	return 0;
}

相关文章:

  • 探秘 Mininet:解锁网络仿真与 SDN 开发的密码
  • 【练习】【栈】牛客NC212914牛牛与后缀表达式
  • Tax with SAP S4HANA (Michael Fuhr, Dirk Heyne, Nadine Teichelmann etc.)
  • DeepSeek 与云原生后端:AI 赋能现代应用架构
  • FunPapers[3]:WWW‘25「快手」生成式回归预测观看时长
  • AI: Cursor是否已奠定AI开发环境的龙头地位?
  • GIT工具学习【1】:基本操作
  • 【Linux】【网络】不同子网下的客户端和服务器通信其它方式
  • Linux之yum详解
  • 算法-二叉树篇17-二叉搜索树中的搜索
  • LSTM预测模型复现笔记和问题记录
  • 通往 AI 之路:Python 机器学习入门-数据结构
  • BGP分解实验·20——BGP选路原则之路径属性
  • Java中Stream流的详细使用介绍
  • 华为OD机试真题:跳房子I (E卷、Java)
  • SpringBoot 项目集成 Prometheus 和 Grafana
  • 项目准备(flask+pyhon+MachineLearning)- 3
  • 计算机毕业设计SpringBoot+Vue.js线上辅导班系统(源码+文档+PPT+讲解)
  • 授权与认证之jwt(五)创建ShiroConfig类
  • 从头开始学SpringBoot—01入门基础配置
  • 【社论】警惕隐形和新型统计造假问题
  • 华生是养了狗,还是藏了枪——《福尔摩斯探案全集》翻译一例
  • 为俄乌一日三通电话,这里成“关键战场”?
  • 英欧再“牵手”,友好“靠美国”
  • 两名游客刻划八达岭长城,被拘5日罚200元
  • 学生靠老干妈下饭、职工餐肉类又多又好?纪委出手整治