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

string类模拟实现

前言:今天我们用C++实现一个增删查改的string类

1.构造,拷贝构造,析构

定义私有成员变量:

	private:
		char* _str;
		size_t _size; //已经有多少个有效字符
		size_t _capacity; //能存多少个有效字符
		static size_t npos;

1.1实现构造函数

		string(const char* str="")
		{
			_size = strlen(str);
			_capacity = _size; // '\0'不是有效字符
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
//string(const char* str = "\0") 错误示范
//string(const char* str = nullptr) 错误示范

1.2 拷贝构造

深浅拷贝问题

浅拷贝,也称值拷贝,编译器只是将对象中的值拷贝过来,会出现多个对象共用同一份资源,当一个对象将那份空间释放掉,另一个对象不知道的时候会出现错误访问。

浅拷贝的问题:1.同一块空间会被析构两次  2.其中一个修改数据会影响另一个

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

1.3 析构函数 

		~string()
		{
			delete[] _str;
			_str = nullptr; //释放后记得置空
			_capacity = _size = 0;
		}

2.运算符重载 

当我们想比较两个string类的大小时,内置的运算符不适用,因此我们将自己实现运算符重载

由于运算符重载比较简单,不做过多阐述,直接将实现的代码写上

2.1  s1<s2

bool operator<(const string& s)
{
	int ret = strcmp(_str, s._str); //比较两个字符串大小,比较ASII码值
	return ret < 0;
}

2.2  s1==s2

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

2.3 s1<=s2

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

2.4 s1>s2 

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

2.5 s1>=s2 

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

2.6 s1!=s2 

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

 3.增删查改

由于我们会在类外面遍历数组之类等操作会用到_size,_capacity,_str等私有成员变量,因此我们将它封装成函数,请看下面实现

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

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

3.1 增加一个字符或者字符串 

插入的时候可能会出现空间不够的时候,因此我们要增容,由于经常会使用增容,防止出现代码冗长,因此我们将增容用一个函数实现,方便调用。

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

}

push_back :尾插一个字符 

void push_back(char ch)
{
	if (_size == _capacity) //判断是否需要增容
	{
		size_t newcapacity =_capacity == 0 ? 2 : _capacity * 2;
		reserve(newcapacity);
	}
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0'; //必须加一个\0 ,否则出现内存泄露
}

append : 尾插字符串

void append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		size_t newcapacity = _size + len;
		reserve(newcapacity);
	}
	strcpy(_str+_size, str);
	_size += len;
}

 由于插入字符和字符串会用到两个不同的函数,因此为了方便我们用"+="来实现

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

上述代码可以看出“+=”的底层实现仍然是调用push_back和append

3.2 删除 

 从pos位置,删除len长度的字符

void erase(size_t pos, size_t len = npos)
{
	if (len >= _size - pos) //判断是否删除pos位置后所有字符
	{
		_str[pos] = '\0';
		_size  = pos;
	}
	else
	{
		while (pos <= _size)
		{
			_str[pos]  = _str[pos + len];
			pos++;
		}
		_size -= len;
	}

}

3.3 查找

 在已知的字符串从pos位置查找所需要的字符或者字符串,返回它的位置,没知道则返回-1

size_t find(char ch, size_t pos = 0)
{
	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)
{
	char* p = strstr(_str, str); //寻找字符串里的子串
	if (p == nullptr)
	{
		return npos;
	}
	else
		return p - _str; //两个指针之间的个数
}

3.4 插入 

在pos位置插入一个字符或者字符串

void insert(size_t pos, char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
		reserve(newcapacity);
	}
	size_t end = _size;
	while (end >= pos)
	{
		_str[end + 1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++;
}
void insert(size_t pos, const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		size_t newcapacity = _size + len;
		reserve(newcapacity);
	}
	size_t end = _size;
	while (end >= pos)
	{
		_str[end + len] = _str[end];
		end--;
	}
	for (size_t i = 0; i < len; i++)
	{
		_str[pos++] = str[i];
	}
	_size += len;
}


http://www.kler.cn/a/322597.html

相关文章:

  • Linux的桌面
  • 如何在MindMaster思维导图中制作PPT课件?
  • 在arm64架构下, Ubuntu 18.04.5 LTS 用命令安装和卸载qt4、qt5
  • Uni-APP+Vue3+鸿蒙 开发菜鸟流程
  • 【Git】Git Clone 指定自定义文件夹名称:详尽指南
  • 蓝桥杯介绍
  • 4.V2X技术
  • 前端开发之装饰器模式
  • 将图片资源保存到服务器的盘符中
  • LLaMA-Factory 使用 sharegpt 格式的数据集
  • nacos 快速入门
  • 【如何学习操作系统】——学会学习的艺术
  • 简单上手vue使用vue-plugin-hiprint进行打印
  • 【FastAPI】使用 SQLAlchemy 和 FastAPI 实现 PostgreSQL 中的 JSON 数据 CRUD 操作
  • 【线程】POSIX信号量---基于环形队列的生产消费者模型
  • windows10使用bat脚本安装前后端环境之msyql5.7安装配置并重置用户密码
  • Meta震撼发布Llama3.2大规模模型
  • 记录QTreeView使用(item勾选,事件,过滤)
  • cubemx配置ADC
  • [3]Opengl ES着色器
  • ST188单光束反射式红外光电传感器心率测量原理
  • 混拨动态IP代理的优势是什么
  • 网络编程(10)——json序列化
  • leetcode721. 账户合并
  • 高级算法设计与分析 学习笔记9 跳表
  • 【论文阅读】RISE: 3D Perception Makes Real-World Robot Imitation Simple and Effective