回顾类与对象:掌握String探索其模拟实现的沉浸式体验

news2024/9/28 1:07:40

在这里插入图片描述

目录

  • 一.STL简介
    • 二.string的模拟实现
      • 1.成员变量与(拷贝)构造、析构函数
      • 2.运算符重载[ ]
      • 3.添加数据与扩容
      • 4.赋值运算符重载及其他重载
      • 5.其他函数

一.STL简介

标准模板库 STL是C++标准库的重要组成部分,stl分为六大组件:算法、容器、迭代器、空间适配器、仿函数 、配接器。包含了里常用的基本数据结构和基本算法,在STL中体现了泛型化程序设计的思想。

二.string的模拟实现

1.成员变量与(拷贝)构造、析构函数

string丰富的函数接口成就了其强大的功能,我们将模拟实现其常用的几个,帮助我们更好的理解string类。string由三个成员变量组成:分别是存储字符串的字符指针、存储有效数据个数的size以及表示当前类容量的capacity:

		size_t _size;
		size_t _capacity;
		char* _str;

首先先来编写构造函数,在我们创建string类时,我们可能会用一个字符串初始类或者仅仅创建:

void test_string6()
{
	ztb::string s1("hello world");
	ztb::string s2;
}

所以我们需要带参和无参两个构造可以合并为一个全缺省的构造函数:

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

这里如果使用初始化列表的话需要注意初始化的顺序要与成员变量的声明顺序一致,不然会出现错误。
我们知道自定义类型的传值,是需要拷贝构造函数来完成的:

	string(const string& t)
		{
			_str = new char[t._capacity + 1];
			memcpy(_str, t._str, t._size + 1);
			_capacity = t._capacity;
			_size = t._size;
		}

类对象在销毁时会自动调用析构函数,完成对象中的资源清理工作:

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

2.运算符重载[ ]

[ ]符号的重载是非常实用的:

	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类型的使得,const对象也可以调用。

3.添加数据与扩容

在向string中添加数据前先要判断容量是否还够,所以先来编写扩容逻辑:

	void reserve(const size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				memcpy(tmp, _str, _size + 1);
				delete[] _str;
				_str = tmp;
				_capacity = n ;///成员不考虑斜杆0
			}
		}

push_back用来尾插一个字符,需要注意的是在判断容量不足扩容时:容量为0的情况(无参构造string类):

	void push_back(char ch)

		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

append函数用来插入一个字符串同push_back一样需要判断容量是否足够:

	void append(const char* str)
		{
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve((len + _size) * 2);

			}
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}

insert函数是在pos位置前插入n个字符或者插入一个字符串:

	void insert(size_t pos, size_t n, char ch)
		{
			assert(pos <= _size);

			if (_size + n > _capacity)
			{
				reserve(_size + n);//至少
			}

			size_t end = _size;
			while (end >= pos && end != npos)end可能会减到负数,所以要制约
			{
				_str[end + n] = _str[end];
				--end;
			}

			for (size_t i = 0; i < n; i++)
			{
				_str[pos+i] = ch;
			}

			_size += n;
		}

其中使用我们在数据结构时常用的挪动数据的逻辑,其中需要注意的是当pos位置为0时end可能会减到-1(end为size_t类型所以是个很大的数字)为了避免这个错误,应限制end。

	void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);//至少
			}

			size_t end = _size;
			while (end >= pos && end != npos)end可能会减到负数,所以要制约
			{
				_str[end + len] = _str[end];
				--end;
			}

			for (size_t i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];
			}

			_size += len;
		}

resize和reserve就像是malloc和calloc的关系,resize可以给指定的_size初始化指定的字符默认为斜杆零。reserve增加的是容量而resize增加的是_size。 resize() 调整字符串的大小。如果字符串长度变小,多余的字符会被截掉。

void resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				reserve(n);

				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

4.赋值运算符重载及其他重载

赋值运算符的现代写法如下啊:如果string s1; string s2("hello world"); s1=s2;
运算符重载通过拷贝构造穿过来的s2通过交换函数,与成员变量做了交换


		void swap(string& tmp)
		{
			std::swap(_str, tmp._str);
			std::swap(_size, tmp._size);
			std::swap(_capacity, tmp._capacity);
		}

		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

+=也是比较常用的,让添加数据变得比较方便:

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

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

比较大小的函数也不可少哦:

		bool operator<(const string& s)
		{
			int ret = memcmp(_str, s._str, _size > s._size ? s._size : _size);

			return ret == 0 ? _size < s._size : ret < 0;
		}

		bool operator==(const string& s)
		{
			return (_size == s._size) && (memcpy(_str, s._str, _size));
		}

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

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

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

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

	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto a : s)
		{
			out << a;
		}
		return out;
	}
	

	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		char ch = in.get();
		// 处理前缓冲区前面的空格或者换行
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}

		//in >> ch;
		char buff[128];
		int i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//in >> ch;
			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

流插入和流提取也模拟出来了,其中使用buff实现了类似空间适配器功能。
当string类对象_size小于某个值时字符串将会存储在数组中,当大于等于某个值时会存储到堆空间中。

5.其他函数

返回起始位置和终数据位置的迭代器iterator在string类中 此迭代器就如同指针:

	typedef char* iterator;
	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;
		}

c_str函数是为了兼容C语言设计的接口,使用C语言可以使用其将string类中的字符串打印出来。

const char* c_str()
	{
		return _str;
	}

返回当前类的_size和_capacity的函数接口:

	size_t size()const
		{
			return _size;
		}

	size_t capacity()const
		{
			return _capacity;
		}

erase函数用来删除pos位置开始指定长度的数据:

	void erase(size_t pos, size_t len = npos)
		{
			assert(pos <= _size);
			if (len == npos || len + pos >= _size)
			{
				_str[pos] = '\0';
				_size = pos;

				//_str[_size] = '\0';这句不用加吧
			}
			else
			{
				size_t end = len + pos;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

如果未指定len则将pos位置起到数据结尾全部删除。
find函数可以从pos位置开始查找指定字符或者字符串:

	size_t find(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;
		}

	size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);
			const char* tmp = strstr(_str + pos, str);
			if (tmp)
			{
				return tmp - _str;
			}
			return npos;

		}

substr函数可以截取一个string对象中的一部分并返回生成的新类:

string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			size_t n = len;
			if (len == npos || len + pos > _size)
			{
				n = _size - pos;
			}
			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; i++)
			{
				tmp += _str[i];
			}
			return tmp;
		}

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

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

相关文章

NLP实战7:seq2seq翻译实战-Pytorch复现

&#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] &#x1f4cc; 本周任务&#xff1a; ●请根据N5、N6周内容&#xff0c;为解码器添加上注意力机制 一、前期准备…

常用分类损失CE Loss、Focal Loss及GHMC Loss理解与总结

一、CE Loss 定义 交叉熵损失&#xff08;Cross-Entropy Loss&#xff0c;CE Loss&#xff09;能够衡量同一个随机变量中的两个不同概率分布的差异程度&#xff0c;当两个概率分布越接近时&#xff0c;交叉熵损失越小&#xff0c;表示模型预测结果越准确。 公式 二分类 二…

【QT】QT搭建OpenCV环境

QT/OpenCV 01、开始之前02、QT03、CMake04、OpenCV05、配置06、测试 01、开始之前 本文版本&#xff1a; 1、QT&#xff1a;Based on Qt 5.12.2 (MSVC 2017, 32 bit)&#xff0c;编译方式是MinGW 2、CMake&#xff1a;cmake-3.27.0-rc4-windows-x86_64.msi 3、OpenCV&#xff1…

2023年值得入手的开放式耳机推荐,蓝牙耳机的选购指南分享推荐

身为一个音乐爱好者&#xff0c;出于对音质和佩戴舒适的追求&#xff0c;也有入手了很多品类的耳机&#xff0c;其中不乏有有线耳机、无线蓝牙耳机&#xff0c;两种不同的音频传输方式大类&#xff0c;其各自所拥有的特性也是不同的。而居于后者的无线蓝牙耳机&#xff0c;在现…

【Java基础教程】(八)面向对象篇 · 第二讲:Java 数组全面解析——动态与静态初始化、二维数组、方法参数传递、排序与转置、对象数组、操作API~

Java基础教程之面向对象 第二讲 本节学习目标1️⃣ 概念1.1 动态初始化1.2 静态初始化 2️⃣ 二维数组3️⃣ 数组与方法参数的传递4️⃣ 数组排序5️⃣ 数组转置6️⃣ 对象数组7️⃣ 数组操作API7.1 数组复制7.2 数组排序 &#x1f33e; 总结 本节学习目标 掌握数组的动态及静…

水库监测中仪器安装及监测结果的要求有哪些

水库监测点位布设需要根据水库运行情况和安全监测的需求来进行&#xff0c;一般分为基础监测点位和重要部位监测点位&#xff0c;基础监测点位主要包括上游水位、上游库水位变幅、库岸稳定以及上下游坝坡稳定等。重要部位监测点位主要包括坝轴线、溢洪道进口和泄水洞出口等部位…

前端报错:“Uncaught SyntaxError: missing ) after argument list“只是参数列表后面缺少 “)”?

报错"Uncaught SyntaxError: missing ) after argument list"&#xff0c;字面翻译过来的意思&#xff1a;语法错误: 参数列表后面缺少 )。 一直以为是少了 一个小括号找了好久 发现并不是 据提示是参数列表的问题&#xff0c;找到文件中存在参数列表的地方。如下图…

如何利用MyBatis完成web项目的环境搭建(导入核心依赖包、日志、编译环境,配置文件以及Druid连接池)

目录 项目环境搭建 servlet实例 核心依赖 导入日志 编译环境 mapper注册 resouces中 dao中 MyBatis配置文件 实例效果 导入配置文件 Druid连接池 Druid连接池是什么&#xff1f; 如何配置Druid连接池&#xff1f; 实体类 实例效果 项目环境搭建 1.在pom.xml中…

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 Proteus仿真小实验&#xff1a; STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 功能&#xff1a; 硬件组成&#xff1a;51单片机 8位数码管MAX7219数码管驱动模块多个按键LED灯蜂鸣器 1.准确测量…

IronOCR for .NET 2023.7.0 Crack

IronOCR for .NET 关于 读取 .NET 应用程序中图像和 Pdf 文本的高级 OCR &#xff08;光学字符识别&#xff09; 库。 IronOCR for .NET enables software engineers to read text content from images & PDFs in .NET applications and Web sites. Read text and barcod…

HarmonyOS/OpenHarmony应用开发-程序包安装、卸载、更新流程

一、应用程序包安装和卸载流程 1.开发者 开发者可以通过调试命令进行应用的安装和卸载&#xff0c;可参考多HAP的调试流程。 图1 应用程序包安装和卸载流程&#xff08;开发者&#xff09; 2.终端设备用户 开发者将应用上架应用市场后&#xff0c;终端设备用户可以在终端设…

python_day4_dict

字典dict:键值对(无重复,无下标索引&#xff09; my_dict {python: 99, java: 88, c: 77, c: 66} my_dict2 {} # 空字典 my_dict3 dict() print(f"my_dict:{my_dict},类型为&#xff1a;{type(my_dict)}") print(f"my_dict2:{my_dict2},类型为&#xff1a;…

AI应用系列--- TalkingPhoto 会说话的照片

利用HeyGen的服务可以生成有趣的Talkingphoto&#xff0c;方法有二&#xff1a; 1、访问HeyGen - AI Video Generator 网站&#xff0c;登录后即可根据提示或者案例生成talkingphoto 2、是使用HeyGen的 Discord​​​​​​机器人&#xff1a;https://discord.com/channels/1…

MySQL数据库期末项目 图书馆管理系统

1 项目需求分析 1.1 项目名称 图书馆管理系统 1.2 项目功能 在以前大多部分图书馆都是由人工直接管理&#xff0c;其中每天的业务和操作流程非常繁琐复杂&#xff0c;纸质版的登记信息耗费了大量的人力物力。因此图书馆管理系统应运而生&#xff0c;该系统采用智能化设计&#…

我来为你揭秘如何将音频转文字才简单

曾经有一位聋哑人士&#xff0c;他很想写一本回忆录&#xff0c;但是因为无法听取自己的回忆录音&#xff0c;他不得不寻找其他方法。于是&#xff0c;他试着用一些软件将他的录音转成文字&#xff0c;但是结果却非常糟糕&#xff0c;充斥着大量错误和不连贯的词语。于是&#…

【大虾送书第一期】《高并发架构实战:从需求分析到系统设计》

目录 ✨写在前面 ✨足够真实的高并发系统设计场景 ✨贴合工作场景的设计文档形式 ✨求同存异的典型系统架构案例 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;免费送书活动专栏地址 写在前面 很多软件工程师的职业规划是成为架构师&a…

手机副业哪些靠谱,推荐几个兼职思路

科思创业汇 大家好&#xff0c;这里是科思创业汇&#xff0c;一个轻资产创业孵化平台。赚钱的方式有很多种&#xff0c;我希望在科思创业汇能够给你带来最快乐的那一种&#xff01; 下面给大家介绍几个靠谱的兼职项目 1.问答答主 知乎、百度、悟空等渠道做问答&#xff0c;…

【手把手】一篇讲清楚FastDFS的安装及使用

分布式存储发展历程 前段时间618活动火热进行&#xff0c;正是购物的好时机。当我们访问这些电商网站的时候&#xff0c;每一个商品都会有各式各样的图片展示介绍&#xff0c;这些图片一张两张可以随便丢在服务器的某个文件夹中&#xff0c;可是电商网站如此大体量的图片&…

XSS漏洞学习笔记

浏览器安全 同源策略 影响源的因素&#xff1a;host,子域名,端口,协议 a.com通过以下代码: <script scrhttp://b.com/b.js> 加载了b.com上的b.js&#xff0c;但是b.js是运行在a.com页面中的&#xff0c;因此相对于当前打开的页面(a.com)来说&#xff0c;b.js的源就应该…

Nodejs 学习笔记

Author&#xff1a;德玛玩前端 Date: 2023-07-06 Nodejs 一、Nodejs概述 1.1、什么是JavaScript 1995年由Netscape公司退出&#xff0c;后经ECMA统一标准的脚本语言。通常狭义上理解的JS是指在浏览器内置的JS解释器中运行的&#xff0c;主要用途是操作网页内容&#xff0c;实…