C++相关概念和易错语法(13)(string的模拟实现)

string由于存在字符串和单字符的概念,使得它的一些接口,实现要比vector多一些。本质上来看string的实现是在顺序表的基础上加入串相关的操作。下面我会分享如何模拟实现string,这可以进一步提高我们对string的熟练程度。

1.构造函数、拷贝构造和析构函数

string::string(const char* str)//构造函数:_size(strlen(str))
{if (_size <= 15)strcpy(_buff, str);else{delete[] _buff, _buff = nullptr;_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}
}string::string(const string& str)//拷贝构造:_size(str._size),_capacity(str._capacity)
{if (_capacity <= 15)strcpy(_buff, str._buff);else{delete[] _buff, _buff = nullptr;_str = new char[_capacity + 1];strcpy(_str, str._str);}
}string::~string()//析构函数
{if (_capacity <= 15)delete[] _buff, _buff = nullptr;elsedelete[] _str, _str = nullptr;_capacity = _size = 0;
}

这里我使用了_buff和_str,使他们分情况存储。当存储字符少于15个的时候(实际开了16个字节,预留一个给\0),就存到_buff里,多了就全部移到_str中。

在这里需要注意的是_str和_buff一定要控制好,当转移数据,从_str转移到_buff时,一定要在释放_str后让_str置空,否则很多地方会出现连续delete两次导致报错,我当时实现的时候这个bug找了很久,如果想像VS2022那样使用两个字符串来管理string的话一定要注意。

2.swap

void string::swap(string& str)//交换{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);std::swap(_buff, str._buff);}

我们实现swap绝大多数情况是为了交换数据,不需要深拷贝,因此直接交换成员变量的所有值即可。swap在后续的复用特别好用,后面会讲到。

3.迭代器的模拟

迭代器是封装的进一步体现,即不需要了解底层数据类型也能正确的用指针的方式对串的内容进行访问。

在串中,我们使用char*和const char*就可以很好地模拟迭代器了

string::iterator string::begin()//迭代器{return _capacity <= 15 ? _buff : _str;}string::iterator string::end()//迭代器{return _capacity <= 15 ? _buff + _size : _str + _size;}string::const_iterator string::begin() const//迭代器{return _capacity <= 15 ? _buff : _str;}string::const_iterator string::end() const//迭代器{return _capacity <= 15 ? _buff + _size : _str + _size;}

4.访问

使用运算符重载可以使我们像普通数组那样去访问数据,注意区分_buff和_str

char string::operator[](size_t pos)//下标取值
{assert(pos < _size);return _capacity <= 15 ? _buff[pos] : _str[pos];
}const char string::operator[](size_t pos) const//下标取值
{assert(pos < _size);return _capacity <= 15 ? _buff[pos] : _str[pos];
}

5.赋值运算符=

这里就能很好的体现出swap的复用优势了,因为赋值意味着原来的数据可以被丢弃,我们借助自动调用析构的特性将可以丢弃的数据和有用的数据交换,使无用的数据存到临时变量(形参)中,在函数结束的时候自动调用析构帮我们销毁了。

	string& string::operator=(string str)//赋值{swap(str);return *this;}

利用复用,我们的代码可以非常简约,但是实际上执行的复杂度是没有改变的,因为复用是函数套用使得表层逻辑简单,底层实现的逻辑是没有变的。

6.insert

insert是string的核心,因为这个函数能够实现几乎所有情况的插入,而erase也可借助insert实现,push_back、append可以直接复用insert,所以这个函数的实现直接影响后续所有插入删除。

string& string::insert(size_t pos, size_t n, char c)//连续插入n个字符{assert(pos <= _size);//防止越界_size += n;if (_size + n > _capacity)reserve((_capacity + n) * 2);for (size_t cur = _size + n; cur > pos + n - 1; cur--){_capacity <= 15 ? _buff[cur] = _buff[cur - n] : _str[cur] = _str[cur - n];}for (size_t count = 0; count < n; count++){_capacity <= 15 ? _buff[pos + count] = c : _str[pos + count] = c;}return *this;}string& string::insert(size_t pos, const string& str, size_t subpos, size_t len)//插入字符串{assert(pos <= _size);assert(subpos <= str._size);if (len > str._size - subpos)len = str._size - subpos;if (len){if (_size + len > _capacity)reserve((_capacity + len) * 2);for (size_t cur = _size + len; cur > pos + len - 1; cur--){_capacity <= 15 ? _buff[cur] = _buff[cur - len] : _str[cur] = _str[cur - len];}for (size_t count = 0; count < len; count++){_capacity <= 15 ? _buff[pos + count] = (str._capacity <= 15 ? str._buff[subpos + count] : str._str[subpos + count]) : _str[pos + count] = (str._capacity <= 15 ? str._buff[subpos + count] : str._str[subpos + count]);}_size += len;}return *this;}

大部分的代码是很简单的,但是在数据挪动的时候下标是一个大难题。这需要我们总结一些技巧。

其中着重理解闭、开、个数之间的关系,可以很好帮我们判断下标问题

7.erase

string& string::erase(size_t pos, size_t len)//删除字符串内容{assert(pos < _size);if (len > _size - pos)swap(string().insert(0, *this, 0, pos));else{string s1, s2;s1.insert(0, *this, 0, pos);s2.insert(0, *this, pos + len);*this = s1 + s2;}return *this;}

借助复用我们可以进一步实现erase,当从pos开始全部删除时用swap,其余情况分成两段insert,最后加起来,这里我提前用了operator+,operator+很好实现,只是我这里讲解的顺序不一样。

insert、erase附加操作这里就不展开了,最后我会分享全部代码。

8.比较

比较运算符重载我们其实只需要实现其中的一两个,其它全部用上层逻辑联系起来,可以很快实现

bool string::operator>(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) > 0;}bool string::operator>=(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) >= 0;}bool string::operator<(const string& str) const{return !(*this >= str);}bool string::operator<=(const string& str) const{return !(*this > str);}bool string::operator!=(const string& str) const{return !(*this == str);}bool string::operator==(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) == 0;}

9.流插入和流提取

ostream& operator<<(ostream& out, const string& str)//流提取{if (str.capacity() <= 15)out << "_buff: ";elseout << "_str: ";for (auto e : str)out << e;out << "    _size:" << str.size() << "    _capacity:" << str.capacity() << endl;return out;}istream& operator>>(istream& in, string& str){char ch = in.get();while (ch != ' ' && ch != '\n'){str += ch;ch = in.get();}return in;}

唯一需要注意的是流插入要使用cin.get(),最好不要用scanf,因为C++和C的缓冲区不互通,也不要用cin>>,读的时候直接忽略了空格,根本停不下来。

全部代码汇总:

string.h

#include <iostream>
#include <assert.h>
using namespace std;namespace my_string
{class string{public:static const int npos;typedef char* iterator;typedef const char* const_iterator;void swap(string& str);string(const char* str = "");string(const string& str);~string();size_t size() const;size_t capacity() const;const char* c_str() const;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;string& operator=(string str);char operator[](size_t pos);const char operator[](size_t pos) const;string& operator+=(const string& s);string& operator+=(char c);string& reserve(size_t newcapacity);string& insert(size_t pos, size_t n, char c);string& insert(size_t pos, const string& str, size_t subpos = 0, size_t len = npos);string& erase(size_t pos, size_t len = npos);string& push_back(char c);string& append(const string& str);string substr(size_t pos = 0, size_t len = npos) const;size_t find(char c, size_t pos = 0) const;size_t find(const string& str, size_t pos = 0) const;size_t find_first_of(const string& str, size_t pos = 0) const;size_t find_first_not_of(const string& str, size_t pos = 0) const;bool operator>(const string& str) const;bool operator>=(const string& str) const;bool operator<(const string& str) const;bool operator<=(const string& str) const;bool operator!=(const string& str) const;bool operator==(const string& str) const;private:char* _str = nullptr;size_t _size;size_t _capacity = 15;char* _buff = new char[16];};ostream& operator<<(ostream& out, const string& str);istream& operator>>(istream& in, string& str);string operator+(const string& s1, const string& s2);}

string.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include "string.h"namespace my_string
{const int string::npos = -1;void string::swap(string& str)//交换{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);std::swap(_buff, str._buff);}string::string(const char* str)//构造函数:_size(strlen(str)){if (_size <= 15)strcpy(_buff, str);else{delete[] _buff, _buff = nullptr;_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}}string::string(const string& str)//拷贝构造:_size(str._size),_capacity(str._capacity){if (_capacity <= 15)strcpy(_buff, str._buff);else{delete[] _buff, _buff = nullptr;_str = new char[_capacity + 1];strcpy(_str, str._str);}}string::~string()//析构函数{if (_capacity <= 15)delete[] _buff, _buff = nullptr;elsedelete[] _str, _str = nullptr;_capacity = _size = 0;}size_t string::size() const//获取size{return _size;}size_t string::capacity() const//获取capacity{return _capacity;}const char* string::c_str() const//取有效字符串地址{return _capacity <= 15 ? _buff : _str;}string::iterator string::begin()//迭代器{return _capacity <= 15 ? _buff : _str;}string::iterator string::end()//迭代器{return _capacity <= 15 ? _buff + _size : _str + _size;}string::const_iterator string::begin() const//迭代器{return _capacity <= 15 ? _buff : _str;}string::const_iterator string::end() const//迭代器{return _capacity <= 15 ? _buff + _size : _str + _size;}string& string::operator=(string str)//赋值{swap(str);return *this;}char string::operator[](size_t pos)//下标取值{assert(pos < _size);return _capacity <= 15 ? _buff[pos] : _str[pos];}const char string::operator[](size_t pos) const//下标取值{assert(pos < _size);return _capacity <= 15 ? _buff[pos] : _str[pos];}string& string::operator+=(const string& s)//字符串自增{insert(_size, s, 0);return *this;}string& string::operator+=(char c)//追加同种字符{insert(_size, 1, c);return *this;}string& string::reserve(size_t newcapacity)//扩容或合理缩容{if (newcapacity < _size)return *this;char* tmp = new char[_size + 1];strcpy(tmp, _capacity <= 15 ? _buff : _str);delete[] _buff, delete[] _str;_str = nullptr, _buff = nullptr;//置空防止后续报错if (newcapacity <= 15)_buff = new char[newcapacity + 1];else_str = new char[newcapacity + 1];strcpy(newcapacity <= 15 ? _buff : _str, tmp);_capacity = newcapacity;delete[] tmp;return *this;}string& string::insert(size_t pos, size_t n, char c)//连续插入n个字符{assert(pos <= _size);//防止越界_size += n;if (_size + n > _capacity)reserve((_capacity + n) * 2);for (size_t cur = _size + n; cur > pos + n - 1; cur--){_capacity <= 15 ? _buff[cur] = _buff[cur - n] : _str[cur] = _str[cur - n];}for (size_t count = 0; count < n; count++){_capacity <= 15 ? _buff[pos + count] = c : _str[pos + count] = c;}return *this;}string& string::insert(size_t pos, const string& str, size_t subpos, size_t len)//插入字符串{assert(pos <= _size);assert(subpos <= str._size);if (len > str._size - subpos)len = str._size - subpos;if (len){if (_size + len > _capacity)reserve((_capacity + len) * 2);for (size_t cur = _size + len; cur > pos + len - 1; cur--){_capacity <= 15 ? _buff[cur] = _buff[cur - len] : _str[cur] = _str[cur - len];}for (size_t count = 0; count < len; count++){_capacity <= 15 ? _buff[pos + count] = (str._capacity <= 15 ? str._buff[subpos + count] : str._str[subpos + count]) : _str[pos + count] = (str._capacity <= 15 ? str._buff[subpos + count] : str._str[subpos + count]);}_size += len;}return *this;}string& string::erase(size_t pos, size_t len)//删除字符串内容{assert(pos < _size);if (len > _size - pos)swap(string().insert(0, *this, 0, pos));else{string s1, s2;s1.insert(0, *this, 0, pos);s2.insert(0, *this, pos + len);*this = s1 + s2;}return *this;}string& string::push_back(char c)//追加单个字符{(*this) += c;return *this;}string& string::append(const string& str)//追加字符串{(*this) += str;return *this;}string string::substr(size_t pos, size_t len) const//子字符串{string tmp = *this;tmp.swap(string().insert(0, *this, pos));return tmp;}size_t string::find(char c, size_t pos) const//找单个字符{for (size_t count = pos; count < _size; count++){if ((*this)[count] == c)return count;}return npos;}size_t string::find(const string& str, size_t pos) const//找字符串{for (size_t count = pos; count < _size - str._size + 1; count++){if ((*this)[count] == str[0]){size_t start = count;for (auto e : str){if (e == (*this)[start++])continue;break;}if (start - count == str._size)return count;}}return npos;}size_t string::find_first_of(const string& str, size_t pos) const//找字符串第一次出现位置{bool stage = false;size_t* count = new size_t[str._size], min = 0, i = 0;for (auto e : str){count[i++] = find(e, pos);}for (size_t k = 0; k < str._size; k++){if (count[k] <= count[min]){min = k;stage = true;}}if (stage)return count[min];return npos;}size_t string::find_first_not_of(const string& str, size_t pos) const//找字符串第一次没出现的位置{size_t cur_pos = 0, next_pos = 0;if (cur_pos = find_first_of(str, pos) != 0)return cur_pos == npos ? 0 : cur_pos - 1;for (size_t count = 1; count < _size; count++){next_pos = find_first_of(str, pos + count);if (cur_pos + 1 != next_pos)return cur_pos + 1;cur_pos = next_pos;}return npos;}bool string::operator>(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) > 0;}bool string::operator>=(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) >= 0;}bool string::operator<(const string& str) const{return !(*this >= str);}bool string::operator<=(const string& str) const{return !(*this > str);}bool string::operator!=(const string& str) const{return !(*this == str);}bool string::operator==(const string& str) const{return strcmp(_capacity <= 15 ? _buff : _str, str._capacity <= 15 ? str._buff : str._str) == 0;}ostream& operator<<(ostream& out, const string& str)//流提取{if (str.capacity() <= 15)out << "_buff: ";elseout << "_str: ";for (auto e : str)out << e;out << "    _size:" << str.size() << "    _capacity:" << str.capacity() << endl;return out;}istream& operator>>(istream& in, string& str){char ch = in.get();while (ch != ' ' && ch != '\n'){str += ch;ch = in.get();}return in;}string operator+(const string& s1, const string& s2)//两个字符串相加{string tmp;tmp += s1, tmp += s2;return tmp;}}

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

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

相关文章

附代码:策略常用-正余弦优化算法

正余弦优化算法作为群智能优化算法的一种, 正弦余弦算法 (sine cosine algorithm, SCA) 是 2016 年由 Mirjalili 提出的一种新型仿自然优化算法, 通过创建多个随机候选解, 利用正余弦函数的数学性质来平衡算法在搜系过程中的全局探索和局部开发能力。该算法具有结构简单、参数少…

docker三种自定义网络(虚拟网络) overlay实现原理

docker提供了三种自定义网络驱动&#xff1a;bridge、overlay、macvlan。 bridge驱动类似默认的bridge网络模式。 overlay和macvlan是用于创建跨主机网络。 支持自定义网段、网关&#xff0c;docker network create --subnet 172.77.0.0/24 --gateway 172.77.0.1 my_n…

PPT大珩助手新功能-生成迷宫

大珩助手是一款功能丰富的办公软件插件&#xff0c;它主要分为两个版本&#xff1a;PPT大珩助手和Word大珩助手。这两个版本都旨在提高用户在处理演示文稿和文档时的效率。 PPT大珩助手 这是一款专门为Microsoft PowerPoint设计的插件。它提供了多种功能&#xff0c;例如素材…

盲人社区生活支持体系:织就一张温暖的网

在当今社会&#xff0c;构建一个全面、包容的盲人社区生活支持体系成为了推动社会进步、保障残障人士权益的重要议题。随着科技的不断革新&#xff0c;一款名为“蝙蝠避障”的辅助软件走进了盲人的日常生活&#xff0c;它如同一位无形的向导&#xff0c;通过实时避障与拍照识别…

element DatePicker 日期选择器设置禁用未来日期,时间范围为60天

需要用到 DatePicker 里面的 picker-options 方法 disabledDate onPick方法 <el-date-pickerv-model"form.xxxx"type"daterange"value-format"yyyy-MM-dd":clearable"false":picker-options"pickerOptions"start-placeho…

运行Android项目时,提示错误: 程序包javax.annotation.processing不存在

今天在运行项目时提示错误: 错误: 程序包javax.annotation.processing不存在 import javax.annotation.processing.Generated; 最后是修改了Android Studio的JDK的路径修改为你安装的JDK路径&#xff0c;完成的修复&#xff1a;

5.23 Linux中超时检测方式+模拟面试

1.IO多路复用的原理&#xff1f; IO多路复用使得一个或少量线程资源处理多个连接的IO事件的技术。对于要处理的多个阻塞的IO操作&#xff0c;建立集合并存储它们的文件描述符&#xff0c;利用单个阻塞函数去监控集合中文件描述符事件到达的情况&#xff0c;&#xff08;如果到…

cPanel中如何移除之前添加的域名

我这边想要移除我之前绑定到主机的域名&#xff0c;但是不知道如何在主机上面进行移除&#xff0c;由于我使用的Hostease的Linux虚拟主机产品默认带普通用户权限的cPanel面板&#xff0c;但是不知道如何在cPanel上操作移除域名&#xff0c;因为也是对于Hostease主机产品不是很了…

Linux磁盘高级操作

RAID RAID存储系统是一种数据存储虚拟化技术&#xff0c;它将多个物理磁盘驱动器组合成一个或多个逻辑单元&#xff0c;以提供数据冗余和/或提高性能。 1. RAID 0 无奇偶校验与冗余&#xff08;磁盘容错&#xff09;的条带存储&#xff08;带区卷/条带卷&#xff09; 由两块…

基于windows通过kind部署轻量级便携式k8s集群

感谢老师的视频教程&#xff1a; 基于windows通过kind部署轻量级便携式k8s集群 wsl windows下的linux wsl --set-default-version 2 wsl --help wsl --list --online wsl --install -d Ubuntu wsl -l -v &#xff08;看看版本是不是2&#xff0c;否则docker那边识别不到&…

从零开始:在线教育系统源码与知识付费小程序开发指南

开发一个功能完善的在线教育系统和知识付费小程序并不是一件简单的事情。今天&#xff0c;小编将从零开始&#xff0c;详细介绍如何开发在线教育系统和知识付费小程序。 一、需求分析与系统设计 在开始编写代码之前&#xff0c;首先需要进行需求分析和系统设计。这一步非常关键…

汽车摄像头智能画质增强解决方案,高品质车载视觉系统

在数字化与智能化浪潮的推动下&#xff0c;汽车行业正经历着一场前所未有的技术革命。其中&#xff0c;车载摄像头作为智能驾驶与安防监控的核心部件&#xff0c;其画质的高低直接关系到行车安全与驾驶体验。美摄科技&#xff0c;作为行业领先的智能图像技术解决方案提供商&…

机器学习圣经PRML作者Bishop推出重磅教材

图1 书籍《Pattern Recognition and Machine Learning》 只要学人工智能的人&#xff0c;必然学机器学习。 只要学机器学习的人&#xff0c;必然看PRML。 PRML为何物&#xff1f; PRML全名《Pattern Recognition and Machine Learning》&#xff0c;一部机器学习领域的内功…

数据库系列之MySQL数据库中内存使用分析

在实际系统环境中&#xff0c;MySQL实例的内存使用随着业务的增长缓慢增长&#xff0c;有些时候并没有及时的释放。本文简要介绍下MySQL数据库中和内存相关的配置&#xff0c;以及分析内存的实际使用情况&#xff0c;以进行应急和调优处理。 1、MySQL内存结构 在MySQL中内存的…

MySQL索引和视图

MySQL索引和视图是关系型数据库MySQL中的两个重要概念。索引用于优化数据库的查询性能&#xff0c;而视图用于提供一个逻辑上的表结构&#xff0c;方便用户查询和操作数据。 索引是一种数据结构&#xff0c;可以加速对数据库表中的数据进行查询的速度。通过创建索引&#xff0…

激光雷达SLAM算法综述

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

BEVFusion的相机工作流中,图像编码之后FPN+ADP网络的作用

在BEVFusion的相机工作流中&#xff0c;图像编码之后会经过一个FPNADP的网络,那么这个结构的作用是什么呢 FPN大家都很熟悉&#xff0c;就是特征金字塔。但是这里还是贴一些来自GPT的废话 在Bird’s Eye View (BEV) 算法中使用的特征金字塔网络&#xff08;FPN, Feature Pyrami…

视频监控管理平台LntonCVS监控视频汇聚融合云平台主要功能应用场景介绍

随着网络技术的不断发展和万物互联时代的到来&#xff0c;视频融合在一些系统集成项目及综合管理应用中变得日益重要。本文以LntonCVS视频融合云平台为案例&#xff0c;探讨视频融合的对象及其应用场景。 1. 视频监控设备 视频监控摄像设备是各种视频应用项目的基础部分。在视…

【Andoird开发】android获取蓝牙权限,beacon,android-beacon-library

iBeacon 最先是苹果的技术&#xff0c;使用android-beacon-library包可以在android上开发iBeacon 技术。 iBeacon的发明意义重大。它是一种基于蓝牙低功耗&#xff08;Bluetooth Low Energy, BLE&#xff09;技术的定位系统&#xff0c;通过向周围发送信号来标识其位置。这项技…

【NumPy】关于numpy.arange()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…