c++领域展开第十五幕——STL(String类的模拟实现)超详细!!!!

c++领域展开第十五幕——STL(String类的模拟实现)超详细!!!!
在这里插入图片描述

文章目录

前言

上篇博客已经简单的介绍了string类的一些接口,并且做了一些了解
同时也刷了一些oj题目,熟练使用一些string的函数
今天我们来模拟实现一下string类
fellow me

string类的模拟实现

首先,string类是在stl库实现之前出现的,后面的stl库的内容大部分都和string类的接口类似
string类比起以前的一些数据结构,多了很多东西
迭代器就是一方面,能够更好的耦合算法和类和对象

string类——迭代器的模拟

我们先来看迭代器以及,string类的成员变量

classstring{public:typedefchar* iterator;//迭代器 begin endtypedefconstchar* const_iterator;constchar*c_str()const// 返回字符常量 {return _str;} iterator begin(){return _str;} iterator end(){return _str + _size;} const_iterator begin()const// 这里是const修饰的迭代器函数{// 在处理一些const修饰的对象时,如果直接访问上面的普通begin return _str;// 会引发权限的 放大 导致程序不能执行 所以这里复写了const的版本} const_iterator end()const{return _str + _size;} size_t size()const// 返回大小 // 这里从const是修饰大小和容量不能被改变{// 权限能缩小 不会放大return _size;} size_t capacity()const// 返回容量{return _size;}private:// char* _str =nullptr;// 字符串 size_t _size =0;// 大小 size_t _capacity =0;// 容量conststatic size_t npos;// 模拟string类里面缺省参数的 npos参数};

string类——默认成员函数

构造函数、析构函数、拷贝构造、运算符重载

在string类标准库里面有好几个版本的构造函数
这里模拟实现了其中两个
析构函数还是比较简单的
主要是拷贝构造函数两个版本来实现

string::string(size_t n,char ch)// 初始化列表:_str(newchar[n +1]),_size(n),_capacity(n){for(size_t i =0; i < n; i++){ _str[i]= ch;} _str[_size]='\0';} string::string(constchar* str)// 构造函数 :_size(strlen(str)){ _capacity = _size; _str =newchar[_size +1];strcpy(_str, str);}string::~string()// 析构函数{delete[] _str; _str =nullptr; _size =0; _capacity =0;} string::string(const string& s)// 拷贝构造 正常我们就是这样实现拷贝构造{// 防止在一些占用空间的参数 比如字符串 _str =newchar[s._capacity +1];// 调用系统的拷贝构造,导致浅拷贝,多次析构同一位置strcpy(_str, s._str);// 引起程序崩溃 _size = s._size; _capacity = s._capacity;}// 这里提供一种新的实现方法 同时也更简单明了// 而且这里的swap函数还有其他的作用 能被复用void string::swap(string& s){ std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity);}// 现代写法 直接复用 string::string(const string& s)// 这里直接定义新的string 如果this和其交换{// 两种方法原理是一样的 string tmp(s._str);swap(tmp);}// 另外还有一个 销毁函数voidclear()// 销毁{ _str[0]='\0'; _size =0;}

下面就是运算符重载部分了

// s1 = s2// s1 = s1void string::swap(string& s){ std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity);} string& string::operator=(const string& s)// 赋值运算符重载{if(this!=&s){ string tmp(s._str);swap(tmp);// 现代写法就是直接复用swap//delete[] _str; // 传统的就是这样一步一步赋值给另外一个对象//_str = new char[s._capacity + 1];//strcpy(_str, s._str);//_size = s._size;//_capacity = s._capacity;}return*this;}// 处理string像访问数组一样 直接堆 [] 进行重载char&operator[](size_t pos)// []重载 {assert(pos < _size);return _str[pos];}constchar&operator[](size_t pos)const// const修饰 不能改变内容{assert(pos < _size);return _str[pos];}// 下面就是一些判断字符串大小的函数 比较大小的函数还是比较简单的bool string::operator==(const string& s)const{returnstrcmp(_str, s._str)==0;// 这里复用了c语言里面的 strcmp 比较函数}bool string::operator!=(const string& s)const{return!(*this== s);}bool string::operator<(const string& s)const{returnstrcmp(_str, s._str)<0;}bool string::operator<=(const string& s)const{return*this< s ||*this== s;}bool string::operator>(const string& s)const{return!(*this<= s);}bool string::operator>=(const string& s)const{return!(*this< s);}

string类——常用函数接口

这里模拟实现一些string类的常用接口函数
reserve、push_back、append、insert、erase、find、substr等接口
实现string的增删改查功能
话不多说,上代码

// reserve函数 预留空间 void string::reserve(size_t n){if(n > _capacity){//cout << "reserve:" << n << endl;char* tmp =newchar[n +1];// 如果原本的空间没有到达指定大小 strcpy(tmp, _str);// 扩容delete[] _str; _str = tmp; _capacity = n;}}// 尾插函数 可以做 +=单个字符的复用函数void string::push_back(char ch){if(_size +1> _capacity)// 如果空间不够 扩容{// 扩容reserve(_capacity ==0?4: _capacity *2);} _str[_size]= ch;// 正常尾插 ++_size; _str[_size]='\0';// 注意\0 结尾}// append函数 在字符串尾部接入字符串 可以做+=字符串函数的复用void string::append(constchar* str){ size_t len =strlen(str);if(_size + len > _capacity){// 扩容 size_t newCapacity =2* _capacity;if(_size + len >2* _capacity){ newCapacity = _size + len;}reserve(newCapacity);}strcpy(_str + _size, str); _size += len;}// +=字符串 和 += 字符 函数的重载 string& string::operator+=(char ch){push_back(ch);return*this;} string& string::operator+=(constchar* str){append(str);return*this;}// 插入函数 在pos 位置 插入 n 个 ch 字符void string::insert(size_t pos, size_t n,char ch){assert(pos <= _size);assert(n >0);if(_size + n > _capacity)// 判断扩容{// 扩容 size_t newCapacity =2* _capacity;if(_size + n >2* _capacity){ newCapacity = _size + n;}reserve(newCapacity);}// 挪动数据/* int end = _size; // pos为size_t 在和int比较时 int 会转为size_t 导致程序出错 while (end >= (int)pos) { _str[end + n] = _str[end]; --end; }*/ size_t end = _size + n;// 这样写代码更健壮 而且end不会到负数部分while(end > pos + n -1){ _str[end]= _str[end - n];--end;}for(size_t i =0; i < n; i++){ _str[pos + i]= ch;} _size += n;/*string tmp(n, ch); insert(pos, tmp.c_str());*/// 这里复用insert的 在pos位置插入字符串}// insert 在pos位置插入字符串void string::insert(size_t pos,constchar* str){//assert(pos <= _size);//size_t n = strlen(str);//if (_size + n > _capacity)//{// // 扩容// size_t newCapacity = 2 * _capacity;// if (_size + n > 2 * _capacity)// {// newCapacity = _size + n;// }// reserve(newCapacity);//}//size_t end = _size + n;//while (end > pos + n - 1)//{// _str[end] = _str[end - n];// --end;//} // 正常写法  size_t n =strlen(str);// 间接扩容insert(pos, n,'x');// 直接用前面的insert复用 先插入 n 个字符 然后再覆盖一下for(size_t i =0; i < n; i++){ _str[pos + i]= str[i];// 直接覆盖 // 这样的代码就简洁很多 复用性高 好溯源}}// 删除函数 erase 指定位置删除长度为 len 的字符串void string::erase(size_t pos, size_t len){if(len >= _size - pos){// 删完了 _str[pos]='\0'; _size = pos;}else{ size_t end = pos + len;// size_t防止int强转while(end <= _size){ _str[end - len]= _str[end];++end;} _size -= len;}}// find查找函数 查找一个字符 size_t string::find(char ch, size_t pos){for(size_t i = pos; i < _size; i++){if(_str[i]== ch){return i;}}return npos;}// 查找一个字符串 size_t string::find(constchar* str, size_t pos){constchar* p =strstr(_str + pos, str);// 这里复用c里面的strstr if(p ==nullptr){return npos;}else{return p - _str;}}//substr 截取字符串 string string::substr(size_t pos, size_t len){ size_t leftlen = _size - pos;if(len > leftlen) len = leftlen; string tmp;// 构造tmp tmp.reserve(len);for(size_t i =0; i < len; i++){ tmp += _str[pos + i];}return tmp;}

string类——输入输出重载

剩下最后一个有点麻烦但又还好的接口
重载输入流和输出流,另外还有getline这个函数

// 输出函数是比较简单的 ostream&operator<<(ostream& out,const string& s){for(auto ch : s){ out << ch;}return out;}// 输入函数就有很多地方需要处理了 istream&operator>>(istream& in, string& s){ s.clear();// 先清除s里面的内容 防止意外// 输入短串,不会浪费空间// 输入长串,避免不断扩容const size_t N =1024;// 如果输入很长的字符串 一步一步输入的话会不断扩容char buff[N];// 这里开一个字符数组存起来 后序直接 += 就行 大大减少了扩容一步到位int i =0;char ch = in.get();while(ch !=' '&& ch !='\n'){ buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} ch = in.get();}if(i >0){ buff[i]='\0'; s += buff;}return in;}// getline 输入字符串 直到指定字符截止 istream&getline(istream& in, string& s,char delim){ s.clear();const size_t N =1024;char buff[N];int i =0;char ch = in.get();while(ch != delim)// 如果字符不是指定截止字符 就一直输入{// 不管是空格还是\n buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} ch = in.get();}if(i >0){ buff[i]='\0'; s += buff;}return in;}

整个string类的模拟实现就差不多到这里啦

总结

今天把string类模拟实现了一遍
在模拟实现过程中,发现了很多的问题
比如哪些重复且作用相同的代码,复用问题
充分利用已有的一些东西,比如std::swap,这个在拷贝构造和赋值重载的时候用的很爽
还有就是一些优化,哪些地方一直构造会费时费力,哪些地方直接复用效果会更好而且效率高
还有就是一些简单的语法知识,比如不经意间的类型强转过程会有意想不到的误区
总之,在模仿中一步一步优化,一步一步学习
借前人之鉴,涨己人之学识,加油

种一棵树最好的时间是十年前,其次是现在
在这里插入图片描述

Read more

Moon VR Video Player中文版下载地址及使用教程:支持8K/12K+多音轨外挂字幕 Moon VR Video Player中文版、Moon VR播放器下载、VR视频播放器推荐、Ste

Moon VR Video Player中文版下载地址及使用教程:支持8K/12K+多音轨外挂字幕 Moon VR Video Player中文版、Moon VR播放器下载、VR视频播放器推荐、Ste

Moon VR Video Player中文版下载地址及使用教程:支持8K/12K+多音轨外挂字幕 关键词:Moon VR Video Player中文版、Moon VR播放器下载、VR视频播放器推荐、SteamVR播放器、多音轨外挂字幕播放器、8K 12K VR播放 作为一个长期折腾的开发者,这段时间一直在找一款真正稳定、格式兼容性强、支持多音轨和外挂字幕的VR播放器。市面上不少播放器要么格式支持有限,要么在8K以上直接卡顿,更别说复杂场景下的字幕和音轨切换。 这次测试的是 Moon VR Video Player(月亮播放器)v835 + 2.8.18 中文版,整体体验确实比很多常见播放器更完整。下面做一次系统梳理,方便需要的朋友参考。 下载地址 链接:https://pan.quark.cn/s/7c80590579cf 一、

By Ne0inhk
从人类视频到机器人跳舞:BeyondMimic 全流程解析与 rl_sar 部署实践

从人类视频到机器人跳舞:BeyondMimic 全流程解析与 rl_sar 部署实践

0. 前言 让人形机器人学会跳舞,听起来像是科幻电影中的场景,但在强化学习和运动模仿技术的推动下,这件事正在变得越来越现实。本文将完整介绍一条从"人类 RGB 视频"到"真实机器人跳舞"的技术链路:首先通过视觉算法从视频中提取人体运动轨迹,然后将人体模型重定向到机器人关节空间,接着在仿真环境中进行强化学习训练,最后在 MuJoCo 中验证并部署到真实的 Unitree G1 人形机器人上。 整条流程涉及四个核心开源项目:GVHMR(视频到人体模型)、GMR(人体到机器人重定向)、BeyondMimic(强化学习训练框架)、以及 rl_sar(仿真验证与真机部署框架)。本文不仅会逐一拆解每个环节的原理和操作步骤,还会深入分析 BeyondMimic 的算法设计,并详细记录将训练产物迁移到 rl_sar 项目中进行 sim2sim 和 sim2real 部署时遇到的关键问题与解决方案。 下图展示了

By Ne0inhk

OpenClaw配置飞书机器人完整指南

OpenClaw配置飞书机器人完整指南 使用openclaw channels add配置飞书机器人需完成插件安装→飞书应用创建→通道配置→事件订阅→发布应用五个核心步骤,以下是可直接执行的详细流程。 文章目录 * OpenClaw配置飞书机器人完整指南 * 一、前置准备 * 二、通道配置(openclaw channels add) * 方法1:交互式向导配置(推荐) * 方法2:非交互式命令配置(适合脚本) * 方法3:手动编辑配置文件 * 三、事件订阅与发布(关键步骤) * 四、测试与验证 * 五、常见问题排查 一、前置准备 1. 飞书开放平台创建应用(获取凭证) 1. 访问飞书开放平台:https://open.feishu.cn/app 2. 创建企业自建应用,填写名称(如"

By Ne0inhk
AI绘画:解锁商业设计新宇宙(6/10)

AI绘画:解锁商业设计新宇宙(6/10)

1.AI 绘画:商业领域的潜力新星 近年来,AI 绘画技术以惊人的速度发展,从最初简单的图像生成,逐渐演变为能够创造出高度逼真、富有创意的艺术作品。随着深度学习算法的不断优化,AI 绘画工具如 Midjourney、Stable Diffusion 等的出现,更是让这一技术走进了大众的视野,引发了广泛的关注和讨论。这些工具不仅操作简便,而且能够在短时间内生成多种风格的绘画作品,大大降低了绘画创作的门槛。 AI 绘画在商业领域展现出了巨大的潜力。据相关数据显示,2021 年中国 AI 绘画市场规模仅为 0.1 亿元,而预计到 2026 年将激增至 154.66 亿元 ,年复合增长率高达 244.1%。这一迅猛的增长趋势,反映出 AI 绘画在商业应用中的广阔前景。越来越多的企业开始认识到 AI 绘画的价值,并将其应用到广告、插画、

By Ne0inhk