[ C++ ] STL---string类的模拟实现

news2024/9/25 17:19:19

目录

string类的成员变量

构造函数

有参构造函数

无参构造函数

析构函数

拷贝构造函数

赋值运算符重载

string类对象的容量操作

string类对象的遍历与访问

[ ] +下标遍历

迭代器遍历

string类对象的增删查改


string类的成员变量

  • string类底层为动态开辟的字符数组,因此第一个成员变量为字符指针char* _str;
  • 需要记录字符数组有效数据的个数,因此第二个成员变量为size_t  _size;
  • 需要记录字符数组的容量,因此第三个成员变量为size_t _capacity;

namespace str
{
	class string
	{
	public:

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

构造函数

有参构造函数

string(const char* str)
	  :_size(strlen(str))
	  ,_capacity(_size)
{
   //当str为空字符串,空字符串包含'\0',此时_capacity=0,需要多开辟一个字节存储字符'\0'
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

无参构造函数

  初始化时未给定初始值,默认给'\0',_size=_capacity=0;

string()
	:_size(0)
	, _capacity(0)
{
	_str = new char[1];
	_str[0] = '\0';
}

使用缺省参数将有参构造函数与无参构造函数合二为一;

string(const char* str = "")
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

析构函数

对于内置类型成员,析构函数不做任何处理,但是_str涉及到资源申请,会导致内存泄漏;

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

拷贝构造函数

为解决浅拷贝析构两次的问题,string类的拷贝构造函数需要实现深拷贝即开辟同样大小的空间,存储相同的内容,使得每个string类的对象都有一份独立的空间;

//拷贝构造函数传统写法
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}
//拷贝构造函数的现代写法思路:
//s2(s1)
//首先采用string类对象s1中_str所指向的字符串构造临时string类对象tmp;
//其次将临时对象tmp与this指针指向的对象进行交换,而原先的地址空间当临时对象tmp出作用域时便可销毁;
void swap(string& s)
{
   std::swap(_str,s._str);
   std::swap(_size,s._size);
   std::swap(_capacity,s._capacity);
}

注意:拷贝构造函数的现代写法依赖于编译器对数据的初始化,若没有对数据进行初始化操作,指针_str的指向的空间是不确定的,交换过程中tmp的_str可能为随机值,最后对临时对象tmp析构可能导致程序崩溃;

//拷贝构造函数的现代写法
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str);
	swap(tmp);
}

赋值运算符重载

赋值重载时两个对象已存在且两个对象已调用构造函数完成初始化,赋值重载本质也是一种拷贝,将一个对象,赋值拷贝给另一个对象,这里同样涉及浅拷贝析构两次的问题,所以需要实现深拷贝;

//赋值运算符重载传统写法
string& operator=(const string& s)
{
	if (this != &s)
	{
       //不能使用原空间指针开辟空间,会导致内存泄漏
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
    }
	return *this;
}
//赋值运算符重载现代写法
//s2=s1  
//s2即*this,由于传值传参,调用拷贝构造函数,s是s1的拷贝
//将s产生的新空间与当前对象s2交换,旧空间当s出作用域时销毁
string& operator=(string s)
{
	swap(s);
	return *this;
}

string类对象的容量操作

		
size_t size() const
{
   return _size;
}
     
size_t capacity() const
{
   return _capacity;
}
        
bool empty() const
{
   return _size==0;
}
        
void clear()
{
    _str[0]='\0';
    _size=0;
}

reserve()只考虑扩容至n,不考虑缩容,扩容逻辑如下:

注:拷贝数据时使用strncpy(),由于strcpy是以'\0'作为拷贝结束的标志,若原字符串_str后面有多个'\0',且只有最后一个'\0'作为标识符,其他的'\0'都是有效字符,而strcpy()只会将第一个'\0'拷贝过来,所以使用strncpy(),避免出错;

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

resize()

  1. n>capacity()时,扩容+尾插(指明字符c,尾插字符c,不指明字符,插入'\0')
  2. size()<n<capacity()时,有效数据的个数size()改变为n,容量capacity()不变;
  3. n<size()时,容量capacity()不变,有效数据的个数size()改变为n,删除数据,保留前n个;
void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
        if(n>_capacity)
        {
		    reserve(n);
        }
	    for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;
		}
		 _size = n;
		 _str[_size] = '\0';
	}
}

string类对象的遍历与访问

[ ] +下标遍历

const char& operator[](size_t pos) const
{
	assert(pos <= _size);

	return _str[pos];
}

char& operator[](size_t pos)
{
	assert(pos <= _size);

	return _str[pos];
}

迭代器遍历

//普通迭代器--迭代器指向的内容可以被修改
typedef char* iterator;

iterator begin()
{
	return _str;
}
iterator end()
{
    return _str + _size;
}
//const迭代器--迭代器指向的内容不可被修改
typedef const char* const_iterator;

const_iterator begin()const
{
	return _str;
}

const_iterator end()const
{
	return _str + _size;
}

string类对象的增删查改

//尾插单个字符
void push_back(char ch)
{
	//判断容量
	if (_capacity == _size)
	{
	    //string s1-->_capacity=0;
		size_t NewCapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(NewCapacity);
	}
	//尾插
	_str[_size] = ch;
	_size++;
	//处理字符'\0'
	_str[_size] = '\0';
}
//尾插字符串str
void append(const char* str)
{
    size_t len = strlen(str);
	//判断容量
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	//将需要插入的字符串str拷贝至原字符数组_str的尾部;
	strcpy(_str + _size, str);
	_size += len;
}
// operator +=
string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}
string& operator+=(const char* s)
{
	append(s);
	return *this;
}
    //pos位置插入单个字符
    void insert(size_t pos, char ch)
	{
		assert(pos < _size);
		//检查容量
		if (_capacity == _size)
		{
			size_t NewCapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(NewCapacity);
		}
		//从后向前向后移动数据
		int end = _size;
		while (end >= (int)pos)
		{
			_str[end + 1] = _str[end];
			end--;
		}
		//插入字符ch
		_str[pos] = ch;
		_size++;
	}
	//pos位置插入字符串
	void insert(size_t pos, const char* str)
	{
		assert(pos < _size);
		//检查容量
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		//移动数据,步长为len
		int end = _size;
		while (end >= (int)pos)
		{
			_str[end + len] = _str[end];
			--end;
		}
		//插入字符串(注意待插入字符串的'\0')
		strncpy(_str + pos, str, len);
		_size += len;
	}

erase()删除数据时,若不传缺省参数,删除pos位置的所有字符;

若传递缺省参数len,当len小于右边剩余的字符,即剩余的长度足够删,则删除指定的pos位置向后的len个数据;

若传递缺省参数len,当len大于等于右边剩余的字符,即剩余的长度不够删除,则删除pos位置之后的所有字符;

static修饰的成员变量为所有类对象所共享,属于整个类,而不属于某个具体对象;

静态成员变量只能在类中声明,类外定义;类中声明时不得给定缺省值,因为缺省值会传递给初始化列表,而初始化列表是成员变量定义的位置并且初始化列表只能初始化某一具体对象的成员变量,所以静态成员变量在类外定义,类中声明;

特殊语法:当静态成员变量被const修饰,便可在类中定义,并且这个常静态成员变量只有整型家族才可以类中如此定义;

需要一个值用于表示不存在的位置,属于整个类,因此定义第四个成员变量npos;

namespace str
{
	class string
	{
	public:

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
        const static size_t npos = -1;//常静态成员变量
	};
}
void erase(size_t pos, size_t len=npos)
{
 //pos+len>_size 删除pos位置之后的所有字符
 //len未指定 删除pos位置之后的所有字符
    assert(pos < _size);
	if (len == npos || pos + len>_size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}
//返回字符ch在string类对象中第一次出现的位置
size_t find(char ch, size_t pos=0) const
{
   for (size_t i = pos; i < _size; i++)
   {
	   if (_str[i] == ch)
	    {
		   return i;
		}
	}

	return npos;
}

//返回子串str在string类对象中首次匹配的第一个字符的位置
size_t find(const char* str, size_t pos=0) const
{
	const char* ptr = strstr(_str + pos, str);
	if (ptr == nullptr)
	{
		return npos;
	}
	else
	{
		return ptr - _str;
	}
}
    
string substr(size_t pos, size_t len)
{
	assert(pos < _size);
	size_t end = pos + len;
	if (len == npos || pos + len >= _size)
	{
		end = _size;
	}

	string str;
	str.reserve(end - pos);
	for (size_t i = pos; i < end; i++)
	{
		str += _str[i];
	}

	return str;
}

string类的常用接口已经模拟实现,欢迎大家批评指正,谢谢!!!

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

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

相关文章

提升水库大坝安全与效率:现代技术云平台的应用

在我国&#xff0c;水库大坝的数量居世界之首&#xff0c;它们在推动国民经济发展中扮演着不可或缺的角色。然而&#xff0c;要想让这些水利工程充分发挥其价值&#xff0c;不仅需要精准的调度与高效的管理&#xff0c;更重要的是要确保其安全无虞。一旦发生事故&#xff0c;后…

【机器学习】基于变色龙算法优化的BP神经网络分类预测(SSA-BP)

目录 1.原理与思路2.设计与实现3.结果预测4.代码获取 1.原理与思路 【智能算法应用】智能算法优化BP神经网络思路【智能算法】变色龙优化算法&#xff08;CSA)原理及实现 2.设计与实现 数据集&#xff1a; 数据集样本总数2000 多输入多输出&#xff1a;样本特征24&#xff…

Java中的I/O讲解(超容易理解)(上篇)

如果想观看更多Java内容 可上我的个人主页关注我&#xff0c;地址子逸爱编程-CSDN博客https://blog.csdn.net/a15766649633?spm1000.2115.3001.5343使用工具 IntelliJ IDEA Community Edition 2023.1.4 使用语言 Java8 代码能力快速提升小方法&#xff0c;看完代码自己敲一…

智慧乡村赋能发展:数字乡村推动农村经济社会持续繁荣

目录 一、智慧乡村的内涵与发展意义 二、智慧乡村赋能发展的路径 1、加强信息基础设施建设 2、推进农业生产智能化 3、提升乡村治理现代化水平 4、推动农村产业融合发展 三、智慧乡村发展面临的挑战与对策 四、智慧乡村发展的未来展望 1、技术融合创新将更加深入 2、…

百度智能云+SpringBoot=AI对话【人工智能】

百度智能云SpringBootAI对话【人工智能】 前言版权推荐百度智能云SpringBootAI对话【人工智能】效果演示登录AI对话 项目结构后端开发pom和propertiessql_table和entitydao和mapperservice和implconfig和utilLoginController和ChatController 前端开发css和jslogin.html和chat.…

怎么在Linux系统下Docker部署Excalidraw白板工具并实现无公网IP远程访问?

文章目录 1. 安装Docker2. 使用Docker拉取Excalidraw镜像3. 创建并启动Excalidraw容器4. 本地连接测试5. 公网远程访问本地Excalidraw5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Ubuntu系统使用Docker部署开源白板工具Excal…

【Linux】实现进度条小程序

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 回车和换行3. 缓冲区4. 进度条4.1 倒计时设置4.2 进度条4.2.1 实现简单进度条4.2.2 进度条完善 5. 附进度条代码5.1 Processbar.h5.2 Processbar.c5.3 Main.c5.4 Makefile 1. 前言 在之前已经了解了 【Lin…

C++ list详解及模拟实现

目录 本节目标 1. list的介绍及使用 1.2 list的使用 2.list的模拟实现 1.对list进行初步的实现 2.头插和任意位置的插入 3.pos节点的删除&#xff0c;头删&#xff0c;尾删 4.销毁list和析构函数 5.const迭代器 6.拷贝构造和赋值操作 3.完整代码 本节目标 1. list的…

怎么做扫码签到活动_一场别开生面的活动签到革新之旅

在数字化飞速发展的今天&#xff0c;传统的签到方式已无法满足人们对高效、便捷、互动性的追求。为此&#xff0c;我们创新性地推出了扫码签到活动&#xff0c;为您带来一场前所未有的智慧互动新体验。 工具/原料 微信小程序 飞多多网站 方法/步骤 一、扫码签到&#xff0c…

【目标跟踪】奇葩需求如何处理(二)

文章目录 一、前言二、奇葩需求2.1、井盖2.2、管线 三、后记 一、前言 在工作中往往出现些奇葩需求。上一篇介绍了一些奇葩需求奇葩需求如何处理&#xff08;一&#xff09; &#xff0c;今天给大家分享一些更奇葩的需求。 二、奇葩需求 2.1、井盖 昨天突然接到一个需求&…

JAVA_会话

会话技术 1.会话: 一次会话包含多次请求和响应 2.功能: 在一次会话的范围内的多次请求&#xff0c;共享数据 3.方式: 3.1.客户端会话技术 Cookie(甜点) 1.概念: 客户端会话技术,将数据保存到客户端 2.快速入门: 1.创建Cookie对象,绑定数据new Cookie(String name,String v…

msvcp140.dll是什么文件?msvcp140.dll丢失如何解决(最新教程)

在玩电脑的时候&#xff0c;经常会碰到一些烦人的东西&#xff0c;比如那个“msvcp140.dll丢失”啥啥啥的。这个东西一出现&#xff0c;整个人都不好了&#xff0c;完全影响了我们愉快电脑生活的节奏。为啥会出现msvcp140.dll丢失的这种情况&#xff0c;怎么解决&#xff0c;还…

精读《架构设计之 DCI》

本期精读文章是&#xff1a;The DCI Architecture 1 引言 随着前端 ES6 ES7 的一路前行&#xff0c; 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式; 我们可以使用函数式 Functional 编程设计&#xff0c;可以使用面向对象 OOP 的设计&#xff0c;可以使用面向…

【C++从练气到飞升】04---拷贝构造函数

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 ⛳️推荐 一、拷贝构造函数的引入 1. 以日期类为例:进行的值拷贝是不会发生错误的 2. 以栈类为例:进行的值拷贝会发现发…

C语言基础(十六)通过指针来输入和获取结构体的变量值

老样子&#xff0c;先看代码 #include <stdio.h> #include <string.h>#define NLEN 30 struct namect{char fname[NLEN];char lname[NLEN];int letters; };void getinfo(struct namect *); void makeinfo(struct namect *ptr); void showinfo(const struct namec…

Kubernetes的Namespace使用

在 Kubernetes 中&#xff0c;命名空间提供了一种用于隔离单个集群中的资源组的机制。资源名称在命名空间内必须是唯一的&#xff0c;但不能跨命名空间。基于命名空间的作用域仅适用于命名空间物体 &#xff08;例如部署、服务等&#xff09;而不是集群范围的对象&#xff08;例…

牛客周赛 Round 37VP(DEF)

D.思维题&#xff1a; 若按照顺序发现很难入手&#xff0c;于是我们不妨先小紫&#xff0c;再让小红反悔即可 假设为cabababbabazbc&#xff0c;如果直接小紫&#xff0c;那么它一定以a开头&#xff0c;于是小红可以先把首尾的a去掉&#xff0c;即czbc,此时可以得到bc,于是小红…

19---时钟电路设计

视频链接 时钟硬件电路设计01_哔哩哔哩_bilibili 时钟电路设计 晶振是数字电路的心脏&#xff0c;数字电路需要一个稳定的工作时钟信号&#xff0c;时钟电路至关重要&#xff01; 1、晶振概述 晶振一般指晶体振荡器。晶体振荡器是指从一块石英晶体上按一定方位角切下薄片&…

基于stable diffusion的IP海报生成

【AIGC】只要10秒&#xff0c;AI生成IP海报&#xff0c;解放双手&#xff01;&#xff01;&#xff01;在AIGC市场发展的趋势下&#xff0c;如何帮助设计工作者解放双手。本文将从图像生成方向切入&#xff0c;帮助大家体系化的学习Stable diffusion的使用&#xff0c;完成自有…

sonar接入maven项目

1、介绍 sonar是一款静态代码质量分析工具&#xff0c;支持Java、Python、PHP、JavaScript、CSS等25种以上的语言&#xff0c;而且能够集成在IDE、Jenkins、Git等服务中&#xff0c;方便随时查看代码质量分析报告。他有如下特性 (1) 检查代码是否遵循编程标准&#xff1a;如命…