【C++】string底层的实现原理(简单详细)

前言

本篇文章我将按照C++文档库中的模块顺序来实现和讲解其实现原理,我们只讲各板块中常用的

目录

一,Member functions(成员函数)

二、Iterators(迭代器)

三、Capacity(容器)

  1. 常见容器的实现
  2. 重点容器代码思想剖析

    四、Element access(成员访问)

    五、Modifiers(修改器)

    1. 常见修改器的实现
    2. 重点修改器代码思想剖析

      六、String operations(字符串操作)
      七、Non-member function overloads(非成员函数重载)
      八、整合版

      一、Member functions(成员函数)
      namespace L
      {
      	class string
      	{
      	public:
      		//构造函数
      		string(const char* str="")
      			:_size(strlen(str))
      		{
      			_capacity = _size;
      			_str = new char[_capacity + 1];
      			strcpy(_str, str);
      		}
      		//拷贝构造
      		string(const string& str)
      		{
      			_str = new char[str._capacity + 1];
      			strcpy(_str, str._str);
      			_capacity = str._capacity;
      			_size = str._size;
      		}
      		//析构函数
      		~string()
      		{
      			delete[] _str;
      			_str = nullptr;
      			_size = _capacity = 0;
      		}
      	private:
      		char* _str;//指向字符串的指针
      		size_t _size;//字符串的有效字符个数
      		size_t _capacity;//字符串的容量大小
      	};
      }
      
      二、Iterators(迭代器)
      namespace L
      {
      	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;
      		}
      	private:
      		char* _str;//指向字符串的指针
      		size_t _size;//字符串的有效字符个数
      		size_t _capacity;//字符串的容量大小
      	};
      }
      

      string的迭代器我这里是用指针来实现的,迭代器主要就是通过begin(),end()来遍历字符串,由于我们实现了begin()和end()两个成员函数,所以我们在遍历的时候可以使用范围for来遍历字符串,范围for的底层就是替换成了迭代器

      三、Capacity(容器)

      1.常见容器的实现

      namespace L
      {
      	class string
      	{
      	public:
      		size_t size() const
      		{
      			return _size;
      		}
      		size_t capacity() const
      		{
      			return _capacity;
      		}
      		bool empty() const
      		{
      			return _size == 0;
      		}
      		void clear()
      		{
      			_size = 0;
      			_str[_size] = '\0';
      		}
      		void resize(size_t n, char ch='\0')
      		{
      			if (n <= _size)
      			{
      				_str[n] = '\0';
      				_size = n;
      			}
      			else
      			{
      				reserve(n);
      				for (size_t i = _size; i < n; i++)
      				{
      					_str[i] = ch;
      				}
      				_size = n;
      			}
      		}
      		void reserve(size_t n)
      		{
      			if (n > _capacity)
      			{
      				char* temp = new char[n+1];
      				strcpy(temp, _str);
      				delete[] _str;
      				_str = temp;
      				_capacity = n;
      			}
      		}
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	};
      }
      

      2.重点代码思想剖析

      这个板块我们重点讲解两个成员函数,resize()和reserve()
      1、resize(size_t n, char ch='\0');
      ①功能描述:当n<=size (字符串的长度)时,只保留字符串的前n个字符;当n>size时 会引发扩容,并且从size位置开始一直到n位置的值都为ch;
      ②实现思想:当n<=size时,直接给n位置赋值成 ‘\0’(因为字符串的结束标识是\0),然后将有效字符个数改为n即可;当n>size时,从字符串的末尾开始添加字符直到size=n为止

      2、void reserve(size_t n);
      ①功能描述:reserve 主要是扩容,避免capacity多次扩容,影响效率;n<size时:不会缩容,也不会扩容;n>capacity时:会扩容
      ②实现思想:当n>capacity时,使用new动态开辟出一块大小为n+1的空间,多开1个空间是用来存放\0的,使用strcpy将原来的数据拷贝到新开的空间中,然后释放掉旧空间

      四、Element access(成员访问)
      namespace L
      {
      	class string
      	{
      	public:
      		char& operator[](size_t n) const
      		{
      			return _str[n];
      		}
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	};
      }
      
      五、Modifiers(修改器)
      1、 常见修改器的实现
      namespace L
      {
      	class string
      	{
      	public:
      		void swap(string& s) 
      		{
      			std::swap(_str, s._str);
      			std::swap(_size, s._size);
      			std::swap(_capacity, s._capacity);
      		}
      		void erase(size_t pos = 0, size_t len = npos)
      		{
      			assert(pos < _size);
      			if (len == npos || len >= _size - pos)
      			{
      				_str[pos] = '\0';
      				_size = pos + 1;
      			}
      			else
      			{
      				strcpy(_str + pos, _str + pos + len);
      				_size -= len;
      			}
      		}
      		void push_back(char ch)
      		{
      			if (_size == _capacity)
      			{
      				reserve(_capacity == 0 ? 4 : 2 * _capacity);
      			}
      			_str[_size] = ch;
      			_size++;
      			_str[_size] = '\0';
      		}
      		void append(const char* str)
      		{
      			size_t len = strlen(str);
      			if (len + _size > _capacity)
      			{
      				reserve(len + _size);
      			}
      			strcpy(_str + _size, str);
      			_size += len;
      		}
      		void insert(size_t pos, char ch)
      		{
      			assert(pos <= _size);
      			if (_size == _capacity)
      			{
      				reserve(_capacity == 0 ? 4 : 2 * _capacity);
      			}
      			size_t end = _size + 1;
      			while (pos < end)
      			{
      				_str[end] = _str[end - 1];
      				end--;
      			}
      			_str[pos] = ch;
      			_size++;
      		}
      		void insert(size_t pos,const char* str)
      		{
      			assert(pos <= _size);
      			int len = strlen(str);
      			if (len + _size > _capacity)
      			{
      				reserve(len + _size);
      			}
      			int end = _size + len;
      			while ((int)pos <= end - len)
      			{
      				_str[end] = _str[end - len];
      				end--;
      			}
      			strncpy(_str + pos, str, len);
      			_size += len;
      		}
      
      		string& operator+=(const char ch)
      		{
      			push_back(ch);
      			return *this;
      		}
      		string& operator+=(const char* str)
      		{
      			append(str);
      			return *this;
      		}
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	};
      }
      

      2、重点修改器的代码剖析
      void insert(size_t pos,const char* str);
      ①功能描述:往字符串中的任意位置插入一个字符串
      ②实现思想:先判断插入进来的字符串的长度+现有的有效字符个数会不会超过该字符串的容量,超过了就扩容,然后从pos位置开始,将其后的字符全部挪动len个字符,然后再使用strncpy拷贝这个字符串到其要插入的位置

      tips:容易犯错的点

      	void insert(size_t pos,const char* str)
      	{
      		assert(pos <= _size);
      		size_t len = strlen(str);
      		if (len + _size > _capacity)
      		{
      			reserve(len + _size);
      		}
      		size_t end = _size + len;
      		while (pos <= end - len)
      		{
      			_str[end] = _str[end - len];
      			end--;
      		}
      		strncpy(_str + pos, str, len);
      		_size += len;
      	}
      

      上面代码出现的问题,如下图解释

      在这里插入图片描述

      六、String operations(字符串操作)
      namespace L
      {
      	class string
      	{
      	public:
      		string substr(size_t pos = 0, size_t len = npos) const
      		{
      			assert(pos < _size);
      			string temp;
      			if (len == npos || len >= _size - pos)
      			{
      				strcpy(temp._str, _str + pos);
      				return temp;
      			}
      			else
      			{
      				for (size_t i = pos; i < len; i++)
      				{
      					temp += _str[i];
      				}
      				_str[len] = '\0';
      				return temp;
      			}
      		}
      		size_t find(char c, size_t pos = 0) const
      		{
      			assert(pos < _size);
      			for (size_t i = 0; i < _size; i++)
      			{
      				if (_str[i] == c)
      				{
      					return i;
      				}
      			}
      			return npos;
      		}
      		size_t find(const char* s, size_t pos = 0) const
      		{
      			assert(pos < _size);
      			char* p=strstr(_str, s);
      			if (p)
      			{
      				return p - _str;//指针相减得到他们之间的字符个数
      			}
      			else
      			{
      				return npos;
      			}
      		}
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	};
      }
      
      七、Non-member function overloads(非成员函数重载)
      namespace L
      {
      	class string
      	{
      	public:
      		
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	};
      	ostream& operator<<(ostream& out, const string& str)
      	{
      		for (size_t i = 0; i <str.size(); i++)
      		{
      			out << str[i];
      		}
      		return out;
      	}
      	istream& operator>>(istream& in, string& str)
      	{
      		char ch;
      		ch = in.get();//get函数用于从输入流上读取一个字符
      		while (ch != ' ' && ch != '\n')
      		{
      			str += ch;
      			ch = in.get();
      		}
      		return in;
      	}
      	void swap(string& s1, string& s2)
      	{
      		s1.swap(s2);
      	}
      }
      

      八、整合版

      #pragma once
      #include<iostream>
      #include<assert.h>
      using namespace std;
      
      namespace L
      {
      	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(const char* str="")
      			:_size(strlen(str))
      		{
      			_capacity = _size;
      			_str = new char[_capacity + 1];
      			strcpy(_str, str);
      		}
      		//s1(s2)
      		string(const string& str)
      		{
      			_str = new char[str._capacity + 1];
      			strcpy(_str, str._str);
      			_capacity = str._capacity;
      			_size = str._size;
      		}
      		~string()
      		{
      			delete[] _str;
      			_str = nullptr;
      			_size = _capacity = 0;
      		}
      
      		void reserve(size_t n)
      		{
      			if (n > _capacity)
      			{
      				char* temp = new char[n+1];
      				strcpy(temp, _str);
      				delete[] _str;
      				_str = temp;
      				_capacity = n;
      			}
      		}
      		void push_back(char ch)
      		{
      			if (_size == _capacity)
      			{
      				reserve(_capacity == 0 ? 4 : 2 * _capacity);
      			}
      			_str[_size] = ch;
      			_size++;
      			_str[_size] = '\0';
      		}
      		void append(const char* str)
      		{
      			size_t len = strlen(str);
      			if (len + _size > _capacity)
      			{
      				reserve(len + _size);
      			}
      			strcpy(_str + _size, str);
      			_size += len;
      		}
      		void insert(size_t pos, char ch)
      		{
      			assert(pos <= _size);
      			if (_size == _capacity)
      			{
      				reserve(_capacity == 0 ? 4 : 2 * _capacity);
      			}
      			size_t end = _size + 1;
      			while (pos < end)
      			{
      				_str[end] = _str[end - 1];
      				end--;
      			}
      			_str[pos] = ch;
      			_size++;
      		}
      		void insert(size_t pos,const char* str)
      		{
      			assert(pos <= _size);
      			int len = strlen(str);
      			if (len + _size > _capacity)
      			{
      				reserve(len + _size);
      			}
      			int end = _size + len;
      			while ((int)pos <= end - len)
      			{
      				_str[end] = _str[end - len];
      				end--;
      			}
      			strncpy(_str + pos, str, len);
      			_size += len;
      		}
      
      		string& operator+=(const char ch)
      		{
      			push_back(ch);
      			return *this;
      		}
      		string& operator+=(const char* str)
      		{
      			append(str);
      			return *this;
      		}
      		size_t size() const
      		{
      			return _size;
      		}
      		size_t capacity() const
      		{
      			return _capacity;
      		}
      		bool empty() const
      		{
      			return _size == 0;
      		}
      		void clear()
      		{
      			_size = 0;
      			_str[_size] = '\0';
      		}
      		void swap(string& s) 
      		{
      			std::swap(_str, s._str);
      			std::swap(_size, s._size);
      			std::swap(_capacity, s._capacity);
      		}
      		char& operator[](size_t n) const
      		{
      			return _str[n];
      		}
      		void resize(size_t n, char ch='\0')
      		{
      			if (n <= _size)
      			{
      				_str[n] = '\0';
      				_size = n;
      			}
      			else
      			{
      				reserve(n);
      				for (size_t i = _size; i < n; i++)
      				{
      					_str[i] = ch;
      				}
      				_size = n;
      			}
      		}
      
      		size_t find(char c, size_t pos = 0) const
      		{
      			assert(pos < _size);
      			for (size_t i = 0; i < _size; i++)
      			{
      				if (_str[i] == c)
      				{
      					return i;
      				}
      			}
      			return npos;
      		}
      		size_t find(const char* s, size_t pos = 0) const
      		{
      			assert(pos < _size);
      			char* p=strstr(_str, s);
      			if (p)
      			{
      				return p - _str;
      			}
      			else
      			{
      				return npos;
      			}
      		}
      		void erase(size_t pos = 0, size_t len = npos)
      		{
      			assert(pos < _size);
      			if (len == npos || len >= _size - pos)
      			{
      				_str[pos] = '\0';
      				_size = pos + 1;
      			}
      			else
      			{
      				strcpy(_str + pos, _str + pos + len);
      				_size -= len;
      			}
      		}
      
      		string substr(size_t pos = 0, size_t len = npos) const
      		{
      			assert(pos < _size);
      			string temp;
      			if (len == npos || len >= _size - pos)
      			{
      				strcpy(temp._str, _str + pos);
      				return temp;
      			}
      			else
      			{
      				for (size_t i = pos; i < len; i++)
      				{
      					temp += _str[i];
      				}
      				_str[len] = '\0';
      				return temp;
      			}
      		}
      	private:
      		char* _str;
      		size_t _size;
      		size_t _capacity;
      	public:
      		static const int npos;
      	};
      	const int string::npos = -1;//静态成员在全局初始化
      
      	ostream& operator<<(ostream& out, const string& str)
      	{
      		for (size_t i = 0; i <str.size(); i++)
      		{
      			out << str[i];
      		}
      		return out;
      	}
      	istream& operator>>(istream& in, string& str)
      	{
      		char ch;
      		ch = in.get();//get函数用于从输入流上读取一个字符
      		while (ch != ' ' && ch != '\n')
      		{
      			str += ch;
      			ch = in.get();
      		}
      		return in;
      	}
      	void swap(string& s1, string& s2)
      	{
      		s1.swap(s2);
      	}
      }
      

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

      如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

      相关文章

      ElasticSearch集群环境

      ElasticSearch集群环境 1、Linux单机 下载地址&#xff1a;LINUX X86_64 (elastic.co) 下载之后进行解压 tar -zxf elasticsearch-7.8.0-linux-x86_64.tar.gz 名字太长了改个名字改成es mv elasticsearch-7.8.0 es因为安全问题&#xff0c;Elasticsearch 不允许 root 用户…

      基于单片机的空气质量检测系统设计(51+4G版)-设计说明书

      设计摘要&#xff1a; 本设计是基于单片机的空气质量检测系统设计涉及以下主要功能&#xff0c;旨在监测甲烷和一氧化碳的浓度&#xff0c;并在浓度过高时采取相应措施&#xff0c;以确保室内空气质量的安全。该系统使用传感器对甲烷和一氧化碳的浓度进行检测。传感器将收集到…

      虚拟化技术 安装和配置StartWind iSCSI目标服务器

      一、实验内容 安装StartWind iSCSI目标服务器配置StartWind iSCSI目标服务器 二、实验主要仪器设备及材料 安装有64位Windows操作系统的台式电脑或笔记本电脑&#xff0c;建议4C8G或以上配置已安装vSphere Client已创建虚拟机并在其上安装CentOS6.5StarWind安装介质starwind.…

      主机和ubuntu连接

      在这里插入图片描述 提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 最近在hw使用Clion连接服务器&#xff0c;就想把自己的电脑配置好&#xff0c;翻出来正点原子的教程&#xff0c;【正点原子】…

      Springboot集成Netflix-ribbon、Enreka实现负载均衡-12

      Netflix Ribbon简介 Netflix Ribbon是Netflix发布的云中间层服务开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。 具体来说&#xff0c;Ribbon是一个客户端负载均衡器&#xff0c;可以在配置文件中列出所有的服务…

      Golang | Leetcode Golang题解之第84题柱状图中最大的矩形

      题目&#xff1a; 题解&#xff1a; func largestRectangleArea(heights []int) int {n : len(heights)left, right : make([]int, n), make([]int, n)for i : 0; i < n; i {right[i] n}mono_stack : []int{}for i : 0; i < n; i {for len(mono_stack) > 0 &&am…

      PMOS和NMOS

      一. MOS管简介 MOS管是场效应管的一种&#xff0c;主要有两种结构形式&#xff1a;N沟道和P沟道&#xff0c;又根据场效应原理的不同&#xff0c;分为耗尽型&#xff08;当栅压为零时有较大漏极电流&#xff09;和增强型&#xff08;当栅压为零&#xff0c;漏极电流也为零&…

      通过物联网管理多台MQTT设备-基于米尔T527开发板

      本篇测评由电子工程世界的优秀测评者“JerryZhen”提供。 本文将介绍基于米尔电子MYD-LT527开发板的网关方案测试。 一、系统概述 基于米尔-全志 T527设计一个简易的物联网网关&#xff0c;该网关能够管理多台MQTT设备&#xff0c;通过MQTT协议对设备进行读写操作&#xff0c;…

      CMakeLists.txt语法规则:改变行为的变量说明一

      一. 简介 前面一篇文章学习了 CMakeLists.txt语法中的 部分常量变量&#xff0c;具体学习提供信息的变量&#xff0c;文章如下&#xff1a; CMakeLists.txt语法规则&#xff1a;提供信息的变量说明一-CSDN博客 CMakeLists.txt语法规则&#xff1a;提供信息的变量说明二-CSD…

      【JavaEE网络】HTTPS详解:从对称与非对称加密到证书认证

      目录 HTTPSHTTPS 是什么“加密” 是什么HTTTPS 的工作过程引入对称加密引入非对称加密引入证书完整流程总结 HTTPS HTTPS 是什么 HTTPS 也是一个应用层协议. 是在 HTTP 协议的基础上引入了一个加密层. HTTP 协议内容都是按照文本的方式明文传输的. 这就导致在传输过程中出现…

      大数据Scala教程从入门到精通第八篇:Scala在IDEA中编写Hello World

      一&#xff1a;Scala在IDEA中编写Hello World object HelloWorld {def main(args: Array[String]): Unit {println("hello world")}}这个对象也单例的。 特殊的Java类库需要import

      利用一下Chat-GPT写两段处理字符串的简单样例ABAP程序。这样可以大大提高工作效率。Chat-GPT的能力真是让人震撼。

      我让Caht-GPT写两段ABAP 程序&#xff0c;第一段程序要求如下&#xff1a; 判读字符串里面是否含有特殊字符&#xff0c;这里说的特殊字符不包括键盘上能够输入的字符&#xff0c;如果有这样的特殊字符则输出来。 DATA: lv_string TYPE string VALUE 你的字符串,lv_result TYP…

      08.2.grafana插件安装

      grafana插件安装 官方网站下载地址(可自定义选择版本)&#xff1a; https://grafana.com/grafana/download/6.3.0?pgget&plcmtselfmanaged-box1-cta1 清华源下载grafana的rpm包 https://mirror.tuna.tsinghua.edu.cn/grafana/yum/el7/ 直接命令行安装,或者包下载下来本地…

      docker runc升级1.1.12

      上传runc-1.1.12制品至中控机 874e970eaa932a97de9888344ae08f24 runc.arm64 将所有节点的runc文件备份 所有节点(包括master+node) vim host [all] 10.1.0.183 ansible_password=Bigdata@Ksyun123 ansible_user=root ansible_port=22 10.1.0.249 ansible_password=Bigdata…

      AcwingWeb应用课学习笔记

      y总Web课链接&#xff1a; VSCode自动格式化 选中Format On Save不起作用 在设置中搜索default formatter&#xff0c;修改成Prettier-Code formatter 标签 文本标签虽然很多&#xff0c;但大部分可看成是预定好样式的<div>和<span>。&#xff08;div也是由sp…

      电脑nvidia驱动和合适版本的duda--自用 回忆版

      参考文献&#xff1a;http://t.csdnimg.cn/ecDuG 内容很多抄的这个&#xff0c;主要害怕链接失效 一、Ubuntu 18.04 安装NVIDIA显卡驱动 1、查看本机显卡能够配置的驱动信息 ubuntu-drivers devices所以可以看出&#xff0c;推荐 nvidia-driver-530 - distro non-free 2、安…

      使用SPI驱动串行LCD的驱动实现(STM32F4)

      目录 概述 1. 硬件介绍 1.1 ST7796-LCD 1.2 MCU IO与LCD PIN对应关系 2 功能实现 2.1 使用STM32Cube配置Project 2.2 STM32Cube生成工程 3 代码实现 3.1 SPI接口实现 3.2 LCD驱动程序实现 3.3 测试程序实现 4 测试 源代码下载地址&#xff1a; https://gitee.com/mf…

      2024年最新趋势跨境电商平台开发需了解的新技术

      随着数字化技术的不断演进和全球市场的日益融合&#xff0c;跨境电商平台开发将面临前所未有的挑战和机遇。为了更好地适应并引领这一发展&#xff0c;开发者需要密切关注2024年最新的技术趋势&#xff0c;以确保他们的平台能够在竞争激烈的市场中脱颖而出。本文将对跨境电商平…

      音视频-H264编码封装- MP4格式转Annex B格式

      目录 1&#xff1a;H264语法结构回顾 2&#xff1a;H264编码补充介绍 3&#xff1a;MP4模式转Annex B模式输出到文件示例 1&#xff1a;H264语法结构回顾 在之前文章里介绍过H264的语法结构。 传送门: 视音频-H264 编码NALU语法结构简介 2&#xff1a;H264编码补充介绍 H…

      镓未来助力联想笔记本GaN适配器标配化,赋能高效用户体验

      镓未来赋能笔记本电脑GaN适配器标配化 据悉&#xff0c;Lenovo 2024年推出搭配的多款新型笔记本原装适配器电源ADL100UDGC3A&#xff0c;采用了镓未来集成型Cascode技术氮化镓功率器件G1N65R150PB。新款方案相较上一代工艺&#xff0c;体积减小23%&#xff0c;重量降低18%&…