C++_string_知识总结(初学)

news2024/9/22 1:17:29

基础认识:

  1. string严格意义上不属于STL容器,其出现的时间早于STL,这也导致了string官方库中部分函数冗余。但是这也体现了语言发展中的一个重要现象——向前兼容。
  2. 和很多STL容器一样,string是一个类,核心是一个顺序表,同时提供一系列接口,以及方法,从而简化在编程中对字符串的相关操作。
  3. 和其他容器不同的是,库中string类使用模板的意义不在于,能使不同内置类型得到相应的实例化,而是针对不同文字的编码,常见的有:uicode(万国码)、UTF-8、UTF-16、UTF-32(数字越大能表示的字符数量就越多)、gbk。
  4. string类中的reserve当n小于capacity时不一定会缩容,如VS上不会,但linux上会。
  5. 谨慎使用insert头插、中间插,replace(如果是平替还好),erase中间删且量小,因为会扩容和移动数据,效率低下。

三种重要的遍历方式

方括号遍历:

比较常见的一种遍历,因为string支持方括号的重载

迭代器遍历:

因为string特殊的物理结构,可以简单地认为,迭代器就是指针。而实际上迭代器是一种封装的思想,使用者不用花太多心思在如何根据需要访问数据上,只需要延续使用指针的传统,从而达到了简化的作用。begin()、end()一组左闭右开的区间,end()指向的是最后一个字符的下一个位置。

范围for遍历(特殊):

范围for遍历的底层其实还是迭代器,在模拟实现中,实现好迭代器后,范围for的写法立即生效。同时注意不用对ch++或者解引用。

模拟实现

不考虑编码、模板,旨在体会string构造的模拟练习:

实现中的要点回顾

  • 在做全缺省传参的时候,缺省值不要给nullptr,应该给'\0',并且斜杠零不应该计入capacity与size中。

  • strcpy会把'\0'也拷到目标中,先拷贝判断。
  • reserve开空间要多开一个留给斜杆零

  • 拷贝、修改字符串的时候一定注意有没有考虑到斜杠零

  • +=运算符重载时reserve不再以二倍来扩容,因为要插入的字符串太大时,两倍有可能达不到。

  • insert是实现时,size_t类型,在和0的比较会出现问题

  • 编译器规定const int/size_t类型可以直接在申明中定义,注意只能是这两种。

  • substr当能超出时记得更新

代码模板回顾:

.cpp

#include"string.h"

namespace Solution
{
	const size_t string::npos = -1;

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			//cout << "reserve:" << n << endl;

			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

		_str[_size] = ch;
		++_size;
		_str[_size] = '\0';
	}

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

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			// 大于2倍,需要多少开多少,小于2倍按2倍扩
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}

		strcpy(_str + _size, str);
		_size += len;
	}

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

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

		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

		// 挪动数据
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}

		_str[pos] = ch;
		++_size;
	}

	void string::insert(size_t pos, const char* s)
	{
		assert(pos <= _size);

		size_t len = strlen(s);
		if (_size + len > _capacity)
		{
			// 大于2倍,需要多少开多少,小于2倍按2倍扩
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}

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

		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = s[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
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}

			_size -= len;
		}
	}

	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);

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

	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);

		// len大于剩余字符长度,更新一下len
		if (len > _size - pos)
		{
			len = _size - pos;
		}

		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}

		return sub;
	}

	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

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

		return out;
	}

	// 17:16继续
	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		const int N = 256;
		char buff[N];
		int i = 0;

		char ch;
		//in >> ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N-1)
			{
				buff[i] = '\0';
				s += buff;

				i = 0;
			}

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

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

		return in;
	}
}

.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include<iostream>
#include<string>
#include<assert.h>
using namespace std;

namespace Solution
{
	class string
	{
	public:
		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;
		}

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

		// 短小频繁调用的函数,可以直接定义到类里面,默认是inline
		string(const char* str = "")
		{
			_size = strlen(str);
			// _capacity不包含\0
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		// 深拷贝问题
		// 
		// s2(s1)
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		// s2 = s1
		// s1 = s1
		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()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		const char* c_str() const
		{
			return _str;
		}

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

		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];
		}

		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 = 0, size_t len = npos);
	private:
		//char _buff[16];
		char* _str;
		size_t _size;
		size_t _capacity;

		//static const size_t npos = -1;
		static const size_t npos;

		/*static const int N = 10;
		int buff[N];*/
	};

	bool operator<(const string& s1, const string& s2);
	bool operator<=(const string& s1, const string& s2);
	bool operator>(const string& s1, const string& s2);
	bool operator>=(const string& s1, const string& s2);
	bool operator==(const string& s1, const string& s2);
	bool operator!=(const string& s1, const string& s2);

	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
}

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

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

相关文章

MySQL:GROUP BY 分组查询

分组查询是SQL中一个非常强大的功能&#xff0c;它允许我们将数据按照一个或多个字段进行分组&#xff0c;并对每个分组进行聚合计算&#xff08;如求和、平均值、最大值、最小值等&#xff09;。在MySQL中&#xff0c;我们使用 GROUP BY 关键字来实现分组查询。 核心语法 SE…

笑出腹肌的饼图绘制秘籍:Matplotlib让你秒变数据烘焙大师!

1. 引言 亲们&#xff0c;还在为数据报告里的饼图头疼吗&#xff1f;别怕&#xff0c;Matplotlib来救场啦&#xff01;它不只是个绘图工具&#xff0c;简直是数据界的魔术师&#xff0c;让你的饼图既专业又有趣。跟我学几招&#xff0c;保证让你的观众边吃边看&#xff0c;爱不…

Linux驱动开发—编写第一个最简单的驱动模块

文章目录 开发驱动准备工作1.正常运行的Linux系统的开发板2.内核源码树3.nfs挂载的rootfs4.得心趁手的IDE 第一个Hello world 驱动程序常见模块的操作命令模块的初始化和清理模块的版本信息模块中的各种宏 示例Hello World代码printk函数解析 使用MakeFile编译驱动模块使用insm…

谷歌账号异常,成功通过验证后这个界面操作指引:建议增加辅助手机和邮箱

许多朋友对下面这个界面都很熟悉&#xff0c;通常是账号被停用后的时候输入账号和密码后&#xff0c;还需要再次输入手机号码验证。而且这个时候输入国内的号码或者谷歌账号绑定的辅助手机号码都不管用&#xff0c;提示此电话号码用于验证的次数过多&#xff0c;或者此电话号码…

链表篇:03-合并有序链表

解题思路&#xff1a; 使用双指针&#xff0c;一个指针指向头节点&#xff0c;然后另外一个指针进行移动。让其头节点保持不动&#xff0c;最后循环遍历两个链表&#xff0c;将其挂到头指针所在的节点上。 temp 守卫节点&#xff0c;用于指向头节点&#xff0c;防止头节点丢…

机械学习—零基础学习日志(高数17——极限局部有界性)

零基础为了学人工智能&#xff0c;真的开始复习高数 这里我们更加详细讲解函数极限性质。上一篇文章里有一些内容还需要进一步补充。 局部有界性 这里是局部有界性的需要注意的事项。第3点&#xff0c;如果函数在闭区间内连续&#xff0c;则必定有界。试想一下&#xff0c;如…

Log4j2漏洞

Log4j2漏洞 步骤一:执行以下命令启动靶场环境并在浏览器访问!!! systemctl start docker cd vulhub/log4j/CVE-2021-44228 vi docker-compose.yml //编写docker-compose.xml的端口和版本号 docker-compose up -d # 访问网址 http://192.168.30.131:8983/solr/#/步骤二:先在自…

MyBatis入门如何使用操作数据库及常见错误(yml配置)

一&#xff0c;什么是MyBatis 是一款优秀的持久层框架&#xff0c;用于简化jdbc的开发 持久层&#xff1a;指的就是持久化操作的层&#xff0c;通常也就是数据访问层&#xff08;dao&#xff09;&#xff0c;也就是用来操作数据库。 也就是MyBatis是让你更加简单完成程序与数…

ECCV 2024前沿科技速递:GLARE-基于生成潜在特征的码本检索点亮低光世界,低光环境也能拍出明亮大片!

在计算机视觉与图像处理领域&#xff0c;低光照条件下的图像增强一直是一个极具挑战性的难题。暗淡的光线不仅限制了图像的细节表现&#xff0c;还常常引入噪声和失真&#xff0c;极大地影响了图像的质量和可用性。然而&#xff0c;随着ECCV 2024&#xff08;欧洲计算机视觉会议…

form表单按钮根据编辑/只读状态显示和隐藏

1. 场景阐述: form表单自定义按钮,在编辑模式显示,在只读模式隐藏 2. 效果: 这里的保存按钮是自定义按钮,在编辑状态的时候显示,非编辑状态下隐藏 3. 解决方案: 如下所示,只需要在按钮中添加odoo自带class类oe_edit_only即可 <header><button type"object"…

桌面管理利器:2024年度待办事项工具评选

国内外主流的10款待办事项桌面工具对比&#xff1a;PingCode、Worktile、滴答清单、番茄ToDo、Teambition、Tower、有道云笔记、TickTick、Any.do、Trello。 在忙碌的工作日中&#xff0c;管理日常任务和待办事项常常让人感到不胜其烦。选择合适的待办事项桌面工具&#xff0c;…

【Python实战因果推断】67_图因果模型2

目录 Are Consultants Worth It? Crash Course in Graphical Models Chains Are Consultants Worth It? 为了展示有向无环图(DAG)的力量&#xff0c;让我们考虑一个更有趣但处理因素并未随机化的情况。假设你是某公司的经理&#xff0c;正在考虑是否聘请顶级咨询顾问。你…

[数据结构] AVL树 模拟实现AVL树

标题&#xff1a;[数据结构] AVL树 && 模拟实现AVL树 水墨不写bug 正文开始&#xff1a; 目录 &#xff08;一&#xff09;普通二叉搜索树的痛点 &#xff08;二&#xff09;AVL树简介 &#xff08;1&#xff09;AVL树的概念 &#xff08;三&#xff09;AVL树的…

LeetCode面试150——189轮转数组

题目难度&#xff1a;中等 默认优化目标&#xff1a;最小化平均时间复杂度。 Python默认为Python3。 目录 1 题目描述 2 题目解析 3 算法原理及程序实现 3.1 暴力求解 3.2 循环链表 3.3 环状替代 3.4 数组翻转 4 题目难度 参考文献 1 题目描述 给定一个整数数组 nu…

运维.Linux.bash学习笔记.数组及其使用

运维专题 Bash Shell数组及其使用 此笔记当前仍在修改和编写。 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:http…

基于N32L406+Freertos+letter_shell终端开源库移植

移植教程 这里首先感谢作者的开源 https://gitee.com/zhang-ge/letter-shell) [Letter shell 3.0 全新出发 | Letter (nevermindzzt.github.io)](https://nevermindzzt.github.io/2020/01/19/Letter shell 3.0全新出发/) 1.复制代码 将litter_shell文件夹中的所有文件复制到…

本地使用Git同步、配合Gitee同步至仓库并下拉到本地(亲手调试,全能跑通)

这几天在公司&#xff0c;同事都在使用Gitee上传项目&#xff0c;进行同步&#xff0c;我也进行了简单学习了解了一下版本控制软件Git&#xff0c;挺不错的&#xff0c;故写个笔记记录一下。 本篇博文主要涉及的内容&#xff1a; 1&#xff0c;本地写代码&#xff0c;通过Git同…

软件测试_接口测试面试题

接口测试是软件测试中的重要环节&#xff0c;它主要验证系统不同模块之间的通信和数据交互是否正常。在软件开发过程中&#xff0c;各个模块之间的接口是实现功能的关键要素&#xff0c;因此对接口进行全面而准确的测试是确保系统稳定性和可靠性的关键步骤。 接口测试的核心目…

树上dp学习总结2

今天也是侥幸刷了两道树上dp的问题&#xff0c;第一个还算简单&#xff0c;但是第二个真的可以说是我碰到的蓝题之首&#xff0c;做了一个晚上我只能留下了不争气的口水&#xff08;太饿了&#xff0c;该吃夜宵了&#xff09; P1131 [ZJOI2007] 时态同步 思路&#xff1a;一开…

RK3568笔记四十九:W25Q64驱动开发(硬件SPI1)

若该文为原创文章&#xff0c;转载请注明原文出处。 一、SPI介绍 串行外设接口 (Serial Peripheral interface) 简称 SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并 且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚。 …