【c++】string深度刨析以及实现

#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
namespace bite
{class string{public://迭代器  //像指针 底层不一定是指针 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():_str(nullptr), _size(0),_capacity(0){}*///string(char* str)    //常量字符串不能直接赋值//	:_str(new char[strlen(str) + 1])  //不是很好,strlen是在运行时,所以不适宜多调用//	, _size(strlen(str))//	,_capacity(strlen(str))//{//	strcpy(_str, str);//}// string(const char* str="\0");  可以但是实际不推荐  因为\0的后面还有\0//string(const char* str=nullptr)  缺省参数,strlen不能初始化为空const char* c_str() const{return _str;}//二合一//构造string(const char* str=""):_size(strlen(str)){_str = new char[_size+1];_capacity = _size;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;//}//s1=s3//string& operator=(const string& s)//{//	char* tmp = new char[s._capacity + 1];//	strcpy(tmp, s._str);//	delete[] _str;//	_str = tmp;//	_size = s._size;//	_capacity = s._capacity;//	return *this;//}//现代写法   本质是一种复用  假他人之手string(const string& s):_str(nullptr){//调用构造string stmp(s._str);//this和tmp进行交换swap(stmp);}//s1==s3//string& operator=(const string&ss)//{//	//调用拷贝构造//	string tmp(ss);//	swap(tmp);//	return *this;//}string& operator=(string tmp){swap(tmp);return *this;}//析构~string(){delete[] _str;_str = nullptr;_size = _capacity = 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 构造const char& operator[](size_t pos)  const{assert(pos < _size);return _str[pos];}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;}_str[n] = '\0';_size = n;}}void reserve(size_t n){if (n > _capacity){char*tmp = new char[n+1];//多开一个‘\0’strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){//扩容2倍/*	if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';*/insert(_size,ch);}void append(const char* str){扩容//int len = strlen(str);//if (_size + len > _capacity)//{//	reserve(_size + len);//}//strcpy(_str + _size, str);//_size += len;insert(_size, str);}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}//pos位置插入void insert(size_t pos, char ch){//等于_size就是尾插assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size+1;////这么写头插时因为end是size_t会发生整形提升//while (end>=pos)/*while (end >= pos){_str[end + 1] = _str[end];--end;}*/while (end>pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;}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 + len;while (end>pos+len-1){_str[end] = _str[end - len];--end;			}strncpy(_str+pos,str,len);_size += len;}//pos位置删除len个字符void erase(size_t pos,size_t len = npos){assert(pos < _size);//pos+len len处于接近阈值的时候会出现溢出的风险//if (len == npos || pos + len >= _size)//解决溢出风险if (len == npos || _size-pos <=len){_str[pos] = '\0';_size = pos;}else{strcpy(_str+pos,_str+pos+len);_size -=len;}}void swap(string& s){//就近原则,去全局找std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//查找位置 ,返回下标size_t find(char ch,size_t pos=0) const{assert(pos < _size);for (int i = pos; i < _size; ++i){if (_str[i]==ch){return i;}}return npos;}size_t find(const char*sub, size_t pos = 0) const{assert(pos < _size);const char* p = strstr(_str+pos, sub);if (pos){return p - _str;}else{return npos;}}string substr(size_t pos=0, size_t len = npos){string sub;//if(pos==npos||len>=_size-pos)if (len >= _size - pos){for (size_t i = pos; i < _size; ++i){sub += _str[i];}}else{for (size_t i = pos; i < pos + len; ++i){sub += _str[i];}}return sub;}void clear(){_size = 0;_str[_size] = '\0';}private:char* _str;size_t _size;size_t _capacity;public://共有的静态成员变量static const int npos;};const int string::npos = -1;bool operator==(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}bool operator>(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret > 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);}void swap(string& x, string& y){x.swap(y);}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch;//会出现死循环的情况 scanf和cin默认空格和换行做分割符无法取到//in >> ch;//get遇到一个字符娶一个字符ch = in.get();while (ch!=' '&&ch!='\n'){s += ch;ch = in.get();}return in;}void test_string1(){string s1("hello world");for (size_t i = 0; i < s1.size(); ++i){cout << s1[i] << " ";}cout << endl;string s2("hello worrld");string::iterator it = s2.begin();while (it!=s2.end()){cout << *it << " ";++it;}cout << endl;for (auto ch : s2){cout << ch << " ";}cout << endl;s2.push_back('s');for (auto ch : s2){cout << ch << " ";}cout << endl;s2.append("xxxxx");for (auto ch : s2){cout << ch << " ";}cout << endl; const string s3("hello world");for (auto ch : s3){cout << ch << " ";}cout << endl;//迭代器不一定是原生指针,typeid可以查看原生类型cout << typeid(std::string::iterator).name() << endl;}void test_string2(){string s1("hello world");s1 += 'x';s1 += "ssss";s1.insert(0,'o');for (auto ch : s1){cout << ch << " ";}cout << endl;}void test_string3(){string s3("hello world");for (auto ch : s3){cout << ch << " ";}cout << endl;s3.erase(5, 3);cout << s3.c_str() << endl;}void test_string4(){string s1("hello world");cout << s1.c_str() << endl;//没实现拷贝构造时//程序崩溃,析构的时候对同一块空间多次析构//发生浅拷贝,值拷贝string s2(s1);cout << s2.c_str() << endl;s2.insert(5, "xxxxx");cout << s2.c_str() << endl;}void test_string5(){string s1("hello world");string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;//深拷贝代价:三次拷贝加一次析构 //swap(s1, s2);//s1.swap(s2);//全局重载swap(s1, s2);cout << s1 << endl;cout << s2 << endl;cout << (s1 == s2) << endl;string s4;string s5;cin >> s4 >> s5;cout << s4 << endl;cout << s5 << endl;}
}

     vs和g++下string结构的说明      

  注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节

vs下string的结构

       string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间       
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

 这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内 部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。

g++下string的结构 

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指 针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数 (解决浅拷贝析构多次的问题)   有引用计数以后,先--引用技术 ,如果减完以后,引用技术==0 代表当前是最后一个管理资源的对象,那就释放。

浅拷贝的修改会影响其他对象的问题 :

检查引用计数,如果等于1,说明资源是自己独占的,不用拷贝
如果大于1,先拷贝再写,这就是写时拷贝

反正都要拷贝,意义是什么?

不是每个对象都会修改,拷贝后不修改就赚   

struct _Rep_base
{size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount;
};

指向堆空间的指针,用来存储字符串。 

写时拷贝

写时拷贝在读取时的缺陷 

扩展阅读

面试中string的一种正确写法 

STL中的string怎么了 

 

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

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

相关文章

面试集中营—JVM篇

一、JVM内存模型 线程独占&#xff1a;栈&#xff0c;本地方法栈&#xff0c;程序计数器; 线程共享&#xff1a;堆&#xff0c;方法区 虚拟机栈&#xff1a;线程私有的&#xff0c;线程执行方法是会创建一个栈阵&#xff0c;用来存储局部变量表&#xff0c;操作栈&#xff0c;…

【Vue3进阶】- Pinia

什么是Pinia Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。它提供了类似于 Vuex 的功能&#xff0c;但比 Vuex 更加简单和直观。 需要在多个组件之间共享状态或数据时使用 Pinia 的 store&#xff0c;这样可以避免 props 和 eventBus 等传统方法…

LeetCode:116.填充每个节点的下一个右侧节点指针

文章目录 1.层次遍历2.使用next层序遍历3.递归方法 LeetCode&#xff1a;116.填充每个节点的下一个右侧节点指针 题目&#xff1a; 示例&#xff1a; 分析题意容易关注到只需要将每层结点连接起来&#xff0c;因此我们只需要把每层结点求出来即可&#xff0c;即使用层次遍历。 …

面试中算法(删去n个数字后的最小值)

有一个整数&#xff0c;从该整数中去掉n个数字&#xff0c;要求剩下的数字形成的新整数尽可能小。 分析&#xff1a;使用栈的特性&#xff0c;在遍历原整数的数字时&#xff0c;让所有数字一个一个入栈&#xff0c;当某个数字需要被删除时&#xff0c;&#xff08;即栈顶数字&g…

【深度学习】Diffusion扩散模型原理解析1

1、前言 diffusion&#xff0c;这几年一直很火的模型&#xff0c;比如这段时间在网上的文生图大模型——Stable diffusion。就是以diffusion作为基底模型&#xff0c;但由于该模型与VAE那边&#xff0c;都涉及了较多了概率论知识&#xff0c;实在让人望而却步。所以&#xff0…

node pnpm修改默认包的存储路径

pnpm与npm的区别 PNPM和NPM是两个不同的包管理工具。 NPM&#xff08;Node Package Manager&#xff09;是Node.js的官方包管理工具&#xff0c;用于安装、发布和管理Node.js模块。NPM将包安装在项目的node_modules目录中&#xff0c;每个包都有自己的依赖树。 PNPM&#xf…

NumPy及Matplotlib基本用法

NumPy及Matplotlib基本用法 导语NumPy导入与生成算术运算N维数组广播元素访问 Matplotlib简单图案绘制多函数绘制图像显示 总结参考文献 导语 深度学习中经常需要对图像和矩阵进行操作&#xff0c;好在python提供了Numpy和Matplotlib库&#xff0c;前者类似一个已经定义的数组…

2024年数维杯B题完整代码和思路论文讲解与分析

2024数维杯数学建模完整代码和成品论文已更新&#xff0c;获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/bgic2nbxs2h41pvt?singleDoc# 2024数维杯数学建模B题45页论文和代码已完成&#xff0c;代码为全部问题的代码 论文包括摘要、问题重述、问题分析、模型假设、…

linux phpstudy 重启命令

[rootLinuxWeb phpstudy]# ./system/phpstudyctl restart 查看命令 1) phpstudy -start 启动小皮面板 2) phpstudy -stop 停止小皮面板 3) phpstudy -restart 重启小皮面板 4) phpstudy -status 查询面板状态 5) phpstudy -in…

pytest(二):关于pytest自动化脚本编写中,初始化方式setup_class与fixture的对比

一、自动化脚本实例对比 下面是一条用例,使用pytest框架,放在一个类中,两种实现方式: 1.1 setup_class初始化方式 1. 优点: 代码结构清晰,setup_class 和 teardown_class 看起来像传统的类级别的 setup 和 teardown 方法。2. 缺点: 使用 autouse=True 的 fixture 作为…

Linux 磁盘分区工具 gdisk / fdisk

fdisk 是传统的 Linux 磁盘分区工具&#xff0c;磁盘容量有2T的大小限制&#xff1b;gdisk 又叫 GPT fdisk, 作为 fdisk 的升级版&#xff0c;主要使用的是GPT分区类型&#xff0c;用来划分容量大于2T的硬盘&#xff0c;本文介绍使用方法。 简介 早期的磁盘使用 fdisk 工具分区…

GitHub搭建免费博客

一、GitHub仓库准备 ​ 搭建博客需要准备两个仓库。一个存放博客图床的仓库&#xff0c;另一个存放博客网站的仓库。 1.1、图床创建 新建仓库 第一步&#xff1a; ​ 第二步&#xff1a; 生成Token令牌 点击右上角头像->Settings->下拉&#xff0c;直到左侧到底&#…

中国地图(2024版审图号地图)和地图变化说明

2024版shp格式审图号地图预览图&#xff1a; 新版中国地图的变化&#xff08;简述&#xff09; 国土面积的增加&#xff1a;新版中国地图显示&#xff0c;中国的国土面积从960万平方公里增加到1045万平方公里&#xff0c;增加了85万平方公里。 九段线变为十段线&#xff1a;…

GT资源-Clock资源

一、Transmitter 时钟分布 XCLK&#xff1a;在使用TX buffer的模式下&#xff0c;XCLK来源于TXOUTCLK。在使用TX bypassing的模式下XCLK来源于TXUSERCLK。TXUSRCLK是GTX/GTH中PCS的内部逻辑时钟。TXUSRCLK2是GT Transceiver 用户侧逻辑时钟。 TXUSRCLK与TXUSRCLK2的关系 FPGA …

无人零售,重塑购物新纪元

在这个快节奏的时代&#xff0c;科技的每一次跃进都在悄无声息地改变着我们的生活方式。而今&#xff0c;无人零售正以雷霆之势&#xff0c;颠覆传统购物模式&#xff0c;为我们带来前所未有的便捷与智能体验。想知道无人零售如何彻底改变我们的购物方式吗&#xff1f;跟随我&a…

字符以及字符串函数

字符以及字符串函数 求字符串长度strlen 长度不受限制的字符串函数strcpystrcatstrcmp 长度受限制的字符串函数strncpystrncatstrncmp 字符串查找strstrstrtok 错误信息报告strerror 字符分类函数字符转换函数tolowertoupper 内存操作函数memcpymemmovememcmpmemset 这篇文章注…

基于Springboot的校园生活服务平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园生活服务平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

(已解决)org.springframework.amqp.rabbit.support.ListenerExecutionFailedException

报错截图 解决方案 1、登录rabbitMQ网址&#xff0c;删除所有队列 2、重启rabbitMQ 亲测有效&#xff01;&#xff01;&#xff01;亲测有效&#xff01;&#xff01;&#xff01;亲测有效&#xff01;&#xff01;&#xff01;

简单问题汇总

一、vector和list 1.vector vector是可变大小数组的序列容器&#xff0c;拥有一段连续的内存空间&#xff0c;并且起始地址不变&#xff0c;因此能高效的进行随机存取&#xff0c;时间复杂度为o(1)&#xff1b;但因为内存空间是连续的&#xff0c;所以在进行插入和删除操作时…

torch.searchsorted

torch.searchsorted 官方文档链接&#xff1a;torch.searchsorted — PyTorch 2.3 documentation 该函数用于在已排序的序列中查找要插入的值的位置&#xff0c;以保持序列的顺序&#xff0c; torch.searchsorted(sorted_sequence, values, *, out_int32False, rightFalse, s…