前言很多人会用std::string但不清楚它底层是一块动态字符数组。本篇博客我们从零实现简化版bit::string实现迭代器、扩容、尾插、追加、插入、下标访问等核心接口吃透顺序容器的设计思想关于 nposnpos 是 string 的静态常量类型是size_t定义写成-1实际是无符号最大值代表不存在的位置staticconstsize_t npos-1;接口实现// 自定义一个命名空间,防止和标准库里的string冲突namespacebit{classstring{public:// 这里放成员函数(各种接口)的声明// ----------------------------------------------// 一些需要频繁调用的函数直接在类内给出实现逻辑// 这里为了博客看起来不冗杂,我就直接单独拎出来写实现逻辑typedefchar*iterator;typedefconstchar*const_iterator;iteratorbegin();iteratorend();const_iteratorbegin()const;const_iteratorend()const;// 构造函数...// 拷贝构造...// 赋值重载...// 析构函数...constchar*c_str()const;voidclear();size_tsize()const;size_tcapacity()const;charoperator[](size_t pos);constcharoperator[](size_t pos)const;//-------------------------------------------------// 其他调用没那么频繁的函数,在类外(声明和定义分离)给出实现逻辑voidreserve(size_t n);voidpush_back(charch);voidappend(constchar*str);stringoperator(charch);stringoperator(constchar*str);voidinsert(size_t pos,charch);voidinsert(size_t pos,constchar*str);voiderase(size_t pos,size_t lennpos);size_tfind(charch,size_t pos0);size_tfind(constchar*str,size_t pos0);stringsubstr(size_t pos0,size_t lennpos);private:char*_strnullptr;size_t _size0;size_t _capacity0;// 加上 static让 npos 成为类的静态常量// 所有 string 对象共享同一份不需要每个对象都存一份节省内存staticconstsize_t npos;};// 现在的位置----bit命名空间中,string类外// static 成员类内只是声明必须在类外面单独定义初始化constsize_t string::npos-1;// 单独定义在命名空间里的全局函数booloperator(conststrings1,conststrings2);booloperator(conststrings1,conststrings2);booloperator(conststrings1,conststrings2);booloperator(conststrings1,conststrings2);booloperator(conststrings1,conststrings2);booloperator!(conststrings1,conststrings2);ostreamoperator(ostreamout,conststrings);istreamoperator(istreamin,strings);// 各种接口的具体实现逻辑...}迭代器 begin / endtypedefchar*iterator;typedefconstchar*const_iterator;iteratorbegin(){return_str;}iteratorend(){return_str_size;}// 函数后const 代表不修改对象const_iteratorbegin()const{return_str;}const_iteratorend()const{return_str_size;}string 的迭代器就是原生的 char* 指针构造 析构 拷贝构造 赋值重载// 构造函数---利用C风格字符串初始化string对象string(constchar*str)// 缺省值为空的C风格字符串,注意是双引号!!{// strlen不计算C风格字符串里的\0_sizestrlen(str);_capacity_size;// 多一个位置是留给\0,为了兼容C风格_strnewchar[_capacity1];// strcpy会拷贝C风格字符串里的\0strcpy(_str,str);}// 析构函数~string(){if(_str){delete[]_str;_strnullptr;}_size_capacity0;}// 深拷贝问题(拷贝构造)//// s2(s1)string(conststrings){_strnewchar[s._capacity1];strcpy(_str,s._str);_sizes._size;_capacitys._capacity;}// 赋值重载stringoperator(conststrings){if(this!s){delete[]_str;_strnewchar[s._capacity1];strcpy(_str,s._str);_sizes._size;_capacitys._capacity;}return*this;} 注意 :为什么构造函数的参数默认值是空字符串而不是nullptr是合法的空 C 风格字符串它在内存里只有一个\0strlen()得到 0后续代码strlen(str)、strcpy可以安全执行如果默认写nullptr不传参数时strnullptr执行strlen(nullptr)直接程序崩溃空指针访问new[容量1]多一个位置保存字符串结束符\0兼容 C 语言c_str()reserve 扩容核心接口// 声明和定义分离时要指定命名空间和类域// 由于这篇博客默认将实现逻辑写在对应命名空间内,所以可以只指定类域// 预先控制底层字符数组的容量voidstring::reserve(size_t n){if(n_capacity){// _capacity不包含\0的位置//// 所以要 new1char*tmpnewchar[n1];// strlen 不包含\0// strcpy 包含\0strcpy(tmp,_str);delete[]_str;_strtmp;_capacityn;}}只开空间、不改变有效数据_size, 只有 n 比当前容量大才扩容push_back 尾插字符、operator (char)voidstring::push_back(charch){// 插入之前先检查是否要扩容if(_size_capacity){// 扩容按照2倍来扩reserve(_capacity0?4:2*_capacity);}_str[_size]ch;_size;// 末尾主动补\0,保证底层始终是合法的C风格字符串_str[_size]\0;}stringstring::operator(charch){push_back(ch);return*this;}复用思想直接调用 push_back避免重复写扩容逻辑append 追加字符串、operator (const char*)// 末尾追加字符串voidstring::append(constchar*str){size_t lenstrlen(str);// 插入前先检查容量if(_sizelen_capacity){// 如果追加后的字符串很长,直接按需要扩容// 如果不长,就按照2倍扩容reserve(_sizelen2*_capacity?_sizelen:2*_capacity);}// 从字符数组最后一个有效元素的后一位开始黏贴strcpy(_str_size,str);_sizelen;}stringstring::operator(constchar*str){append(str);return*this;}insert 插入难点挪动数据// 在任意位置插入字符voidstring::insert(size_t pos,charch){assert(pos_size);// 插入前先检查是否需要扩容if(_size_capacity){reserve(_capacity0?4:2*_capacity);}// 要连同末尾的\0一起拷贝size_t end_size1;// 将pos位置空出来存放要插入的数据while(endpos){// 将pos位置及其以后的数据// 从尾巴开始一个一个往后移动一位_str[end]_str[end-1];end--;}_str[pos]ch;_size;// 末尾主动补\0,保证底层始终是合法的C风格字符串_str[_size]\0;}//------------------------------------------------voidstring::insert(size_t pos,constchar*str){assert(pos_size);size_t lenstrlen(str);// 插入前先检查是否扩容if(_sizelen_capacity){reserve(_sizelen2*_capacity?_sizelen:2*_capacity);}// 要连同末尾的\0一起拷贝size_t end_sizelen;// pos~poslen-1 这len个空间是留给要插入的新字符串的//// 将下标大于等于 poslen-1的元素整体往后移动len个单位长度//// 从数组末尾移动,防止数据覆盖while(endposlen-1){_str[end]_str[end-len];end--;}// 移动完成// 现在从pos位置开始填数据for(size_t i0;ilen;i){_str[posi]str[i];}_sizelen;// 末尾主动补\0,保证底层始终是合法的C风格字符串_str[_size]\0;}✅ 面试重点必须从后往前搬数据如果从前向后移动原有数据会被覆盖丢失✅ 时间复杂度 O (N)所以 string 中间插入效率很低operator[ ] 下标重载// 类内实现charoperator[](size_t pos){assert(pos_size);return_str[pos];}constcharoperator[](size_t pos)const{assert(pos_size);return_str[pos];}返回引用可读可改const 版本保证 const 对象只能读不能修改erase( )字符串左闭右开减出来的就是中间元素个数voidstring::erase(size_t pos,size_t lennpos){// _size位置是\0的位置,len左闭右开减出来的长度是0,允许出现这个位置// reserve开的空间也包含了\0的位置assert(pos_size);// 分两种情况: 删除的长度超过后续字符串 or 删除的长度不超过后续字符串// 左闭右开 减出来的是中间字符串长度if(len_size-pos||lennpos){// clear接口的逻辑_str[pos]\0;_sizepos;}// 删除的长度不超过后续字符串else{size_t iposlen;// 后面的字符往前覆盖for(;i_size;i){_str[i-len]_str[i];}_size-len;_str[_size]\0;}}clear( ), size( ), capacity( ), c_str( )// 这些需要频繁调用的接口直接在类内实现voidclear(){_str[0]\0;_size0;}size_tsize()const{return_size;}size_tcapacity()const{return_capacity;}// 把 string 转回 C 风格 char * 字符串很多老接口需要constchar*c_str()const{return_str;}find( )查找字符size_t string::find(charch,size_t pos0){// _size位置代表数组里\0的位置assert(pos_size);for(intipos;i_size;i){if(_str[i]ch){returni;}}returnnpos;}查找字符串补充的C语言函数接口strstr#includestring.hchar*strstr(constchar*haystack,constchar*needle);作用在主串 haystack里查找子串needle第一次出现的位置返回匹配起始地址找不到返回NULL返回值细节 :找到返回haystack中该子串首字符指针没找到NULL特殊规则needle是空字符串直接返回 haystack 本身C 标准规定size_t string::find(constchar*str,size_t pos0){assert(pos_size);// 在主字符串中查找字串constchar*ptrstrstr(_strpos,str);if(ptrnullptr){returnnpos;}else{// 指针相减得到对应下标returnptr-_str;}}substr ( pos , len )// 截取子串string string::substr(size_t pos0,size_t lennpos){assert(pos_size);// 确认有效长度if(len_size-pos||lennpos){len_size-pos;}string sub;sub.reserve(len);for(size_t i0;ilen;i){sub_str[posi];}returnsub;}关系运算符号的重载先重载和,然后其他关系运算符直接复用这两个// 放在命名空间内,类外实现的全局函数// strcmp是针对C风格字符串的比较,所以先转化类型booloperator(conststrings1,conststrings2){returnstrcmp(s1.c_str(),s2.c_str())0;}booloperator(conststrings1,conststrings2){returnstrcmp(s1.c_str(),s2.c_str())0;}booloperator(conststrings1,conststrings2){returns1s2||s1s2;}booloperator(conststrings1,conststrings2){return!(s1s2);}booloperator(conststrings1,conststrings2){return!(s1s2);}booloperator!(conststrings1,conststrings2){return!(s1s2);}流插入运算符与流提取运算符的重载// 流插入运算符的重载ostreamoperator(ostreamout,conststrings){// 用范围for遍历 stringfor(autoch:s){coutch;}// 返回流引用,支持链式调用returnout;}// 流提取运算符的重载istreamoperator(istreamin,strings){// 默认以空格,换行作为分隔符号// 一个一个字符读取// 先将读到的字符存入缓冲数组// 等到缓冲数组满了再进字符串里// 先清空字符串的内存s.clear();// 这么做不会把缓冲数组的大小写死,方便自定义constintN256;charbuff[N];inti0;charch;// 读取一个输入的字符,包含空格与换行符号chin.get();// 读到空格与换行符号就停止读取while(ch! ch!\n){buff[i]ch;if(iN-1){buff[i]\0;sbuff;i0;}chin.get();}if(i0){buff[i]\0;sbuff;}// 返回流引用,支持链式调用returnin;}
企业数字化 ERP 产品动态
相关推荐
毕马威-基于平衡计分卡的绩效管理方法研讨 做 HR、企业管理或部门负责人的宝子集合!挖到毕马威出品的基于平衡计分卡的绩效管理方案,39 页干货把战略落地、指标设计、考核流程讲透,告别 “只看财务、忽略长远” 的低效管理~
不管是搭建企业绩效管理体系,还是优化… · 2026/11/5 5:02:19
电工证包通过技巧 电子证书怎么考不被坑 电工证包通过技巧 电子证书怎么考不被坑 不知道去哪报名怕被中介坑,电工证考试真的让人头疼。尤其是像你这样刚毕业的许昌工程类学生,面对市场上各种培训机构、中介和所谓的“包通过”服务,容易被信息轰炸,选错方向。其实, 电子证书… · 2026/11/11 1:24:57
电业局换电工证官方回应:怕考不过白交培训费?实操干货来了 电业局换电工证官方回应:怕考不过白交培训费?实操干货来了 怕考不过白交培训费?这事儿真不是危言耸听。去年郑州有位房建工程的电工老王,花了3800块报了个“电业局换电工证”培训班,结果考试一紧张,连基本的漏电保护器操作都搞错了,直接被刷下来,… · 2026/11/11 1:24:57
贵州建筑电工证考试真实经历:报名怕被坑?老电工带你避坑 贵州建筑电工证考试真实经历:报名怕被坑?老电工带你避坑 你是不是也遇到过这种情况:听说要考贵州建筑电工证,但不知道去哪儿报名,怕被中介坑?别急,我就是干了十多年特种作业的电工,经历过无数次考试和补办,现在就把我的 真实经历… · 2026/11/11 1:24:57
甘肃考电工证有补贴吗 电工证电子证书怎么领 甘肃考电工证有补贴吗 电工证电子证书怎么领 工地太忙根本没时间复习考试,很多一线电工师傅都遇到过这种情况,尤其是那些刚刚从其他岗位转岗到电工岗位的,比如周口地区的不少电工,他们一边要忙工地的活,一边还得抽空备考。如果再听说甘肃考电工证有补贴… · 2026/11/11 1:24:57
没电工证可以投简历吗官方回应来了,外省电工证如何转接 没电工证可以投简历吗官方回应来了,外省电工证如何转接 之前考的证在外省能不能转过来?这个问题在建筑工人群体里一直存在,尤其是像平顶山这样的地级市,不少工友都有在外省工作或学习的经历,手中持有的电工证、焊工证等特种作业证书可能来自其他省市,但… · 2026/11/11 1:23:57
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/10 2:16:10
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/10 17:46:39
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/10 17:11:43
_中职电工证最新政策解读 中职电工证在哪考?工地太忙根本没时间复习考试的救星来了 工地太忙根本没时间复习考试,这是很多正在备考 中职电工证 的建筑工友的真实写照。你不是不想考,而是真的没时间。但别担心,这期内容是 新乡初次报考人员… · 2026/11/11 0:00:52
昆明电工证培训价格值不值得考?老电工告诉你真相 昆明电工证培训价格值不值得考?老电工告诉你真相 之前考的证在外省能不能转过来?这个问题是很多在外地打工、换城市生活、或者是想在昆明发展的朋友最关心的。尤其是现在云南发展快,很多企业都在招持证上岗的电工,如果你手头有证但不在本地,那这个证是不… · 2026/11/11 0:00:52
特种电缆张工网上查询全攻略:外省证如何转过来 特种电缆张工网上查询全攻略:外省证如何转过来 之前考的证在外省能不能转过来?这是很多特种作业从业者最关心的问题,尤其是像特种电缆张工这类证书,直接关系到日常工作是否合规、能否顺利晋升。很多小伙伴因为证书归属地的问题,陷入职业发展的瓶颈,甚至… · 2026/11/11 0:00:52
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 20:19:29
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 19:22:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 17:00:47