【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);}
      }
      

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

      如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

      相关文章

      超详细 springboot 整合 Mock 进行单元测试!本文带你搞清楚!

      文章目录 一、什么是Mock1、Mock定义2、为什么使用3、常用的Mock技术4、Mokito中文文档5、集成测试和单元测试区别 二、API1、Mockito的API2、ArgumentMatchers参数匹配3、OngoingStubbing返回操作 三、Mockito的使用1、添加Maven依赖2、InjectMocks、Mock使用3、SpringbootTes…

      KAN神经网络简短介绍

      KANs简介 Kolmogorov-Arnold Networks (KANs) 是一种创新的神经网络模型&#xff0c;它挑战了传统多层感知器(MLPs)的设计&#xff0c;通过将激活函数从节点转移到边上来提升模型的性能和可解释性。KAN的核心在于&#xff0c;其所有权重参数均被单变量的样条函数代替&#xff…

      算法题解记录24+++二叉树的右视图(百日筑基)

      题目描述&#xff1a; 题目难度&#xff1a;中等 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4]示例 2: 输入: [1,null,3]…

      picoCTF-Web Exploitation-Trickster

      Description I found a web app that can help process images: PNG images only! 这应该是个上传漏洞了&#xff0c;十几年没用过了&#xff0c;不知道思路是不是一样的&#xff0c;以前的思路是通过上传漏洞想办法上传一个木马&#xff0c;拿到webshell&#xff0c;今天试试看…

      【C/C++】C/C++ 车票售票系统设计与实现(源码+课件)【独一无二】

      &#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

      Linux部署

      先把需要的东西准备好&#xff1a; 第一步解压tomcat&#xff1a; tar -zxvf apache-tomcat-8.5.20.tar.gz 第二步解压jdk: tar -zxvf jdk-8u151-linux-x64.tar.gz 第三步配置Java环境变量&#xff1a; vim /etc/profile 把下面代码放进去&#xff1a; export JAVA_HOME/root…

      【解决】:git clone项目报错fatal: fetch-pack: invalid index-pack output

      象&#xff1a;之前一直使用gitee将个人学习和工作相关记录上传到个人gitee仓库&#xff0c;一直没出现过问题。直到有一天换电脑重新拉取代码发现出了问题&#xff0c;具体如下图&#xff1a; 原因分析&#xff1a; 经过查询发现主要原因是因为git clone的远程仓库的项目过大…

      台阶仪测量膜厚原理及优势

      台阶仪&#xff0c;也称为探针式轮廓仪或接触式表面轮廓测量仪&#xff0c;主要用于台阶高、膜层厚度、表面粗糙度等微观形貌参数的测量。 台阶仪的工作原理 台阶仪的核心部件是一个精密的触针或探针&#xff0c;它被安装在一个高度可调的支架上。当触针沿被测表面轻轻滑过时…

      搭建 IIS + asp +access 网站

      搭建 IIS asp access 网站 一、什么是 asp二、asp 的组成三、asp 说明四、什么是access五、搭建环境六、问题一七、问题二八、网站展示九、IIS 页面展示十、IIS 功能展示 欢迎关注公总号【云边小网安】 一、什么是 asp asp&#xff1a;即 Active Server Pages&#xff0c;是…

      14.跳跃游戏Ⅱ

      文章目录 题目简介题目解答解法一&#xff1a;贪心算法动态规划代码&#xff1a;复杂度分析&#xff1a; 题目链接 大家好&#xff0c;我是晓星航。今天为大家带来的是 跳跃游戏Ⅱ 相关的讲解&#xff01;&#x1f600; 题目简介 题目解答 解法一&#xff1a;贪心算法动态规划…

      Line Buffer概述

      buffer在芯片物理上一般指的是SRAM&#xff0c;也可以指寄存器组。buffer的作用是用来在逻辑芯片上暂时存储数据&#xff0c;但不会是大量的数据。如果是大量数据一般会使用DRAM&#xff08;典型的指DDR&#xff09;作为存储芯片&#xff0c;用来存储大密度数据。line buffer可…

      Linux开发--Linux内核开发移植

      Linux内核开发移植 Linux内核版本变迁及其获得 Linux是最受欢迎的自由电脑操作系统内核&#xff0c; 是一个用C语言写成&#xff0c; 并且符合POSIX标准的类Unix操作系统 Linux是由芬兰黑客Linus Torvalds开发的&#xff0c; 目的是尝试在英特尔x86架构上提供自由免费的类Un…