【C++干货铺】剖析string | 底层实现
=========================================================================
个人主页点击直达:小白不是程序媛
C++专栏:C++干货铺
代码仓库:Gitee
=========================================================================
目录
成员变量
clsss string
{
private:
char* _str;
size_t _size;
size_t _capacity;
public:
const static size_t npos;
}
string实际上是一个字符类型的顺序表,因此需要动态开辟空间。_str是指向动态开辟的空间,_size用来表示有效数据的个数,_capacity表示容量。
成员函数
构造和拷贝构造
string(const char * str="")
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity+1];
strcpy(_str, str);
}
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_capacity = s._capacity;
_size = s._size;
}
全缺省有参构造即可以实现有参构造,又可以实现无参构造。使用无参构造时,这里的str什么都不用放,里面面默认含有一个'\0'.这里使用初始化列表,要注意初始化的顺序和成员变量的顺序相同。
赋值重载
String& operator=(const String& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return* this;
}
这里不可以直接对_str重新开辟空间,否则会造成内存泄漏,需要一个中间变量。
析构函数
~string()
{
delete _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
因为要动态内存开辟,所以要手动释放内存。
operator[ ]
char& operator[](size_t pos)
{
return _str[pos];
}
返回字符的引用,用来读写字符。
const char& operator[](size_t pos)const
{
return _str[pos];
}
返回字符串的引用,const修饰适用于静态创建的对象,只读不可写。
size
size_t size()const
{
return _size;
}
配合operator[ ]可以实现,对一个对象的读写。
迭代器
typedef char* iterator;
typedef const char* c_iterator;
c_iterator begin()const
{
return _str;
}
c_iterator end()const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
begin和end函数分别返回字符串的头指针和尾指针,配合循环实现迭代器的读和写。
reserve(扩容函数)
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
当传入的值大于容量时进行扩容,不可以直接对_str扩容要使用中间变量,防止内存泄漏。
push_back(尾插函数)
void push_back(char a)
{
//先判断容量满没满
if (_capacity == _size)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = a;
_size++;
_str[_size] = '\0';
}
通过_size和_capacity判断容量是否满了,满了的话调用扩容函数。这里要注意如果是一个空对象,进行尾插时,_capacity为0要对_capacity使用三目操作符判断。并且要在尾插结束后加入'\0',因为字符串的结尾要为'\0'。
append(尾插一个字符串)
void append(const char* str)
{
size_t len=strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
strcpy(_str + _size, str);
_size += len;
}
求出插入字符串的长度和有效数据相加判断容量是否足够,不够的话调用reverse函数扩容。
最后在尾指针的位置开始将插入的字符串拷贝进去。
pos位置插入字符
void insert(size_t pos, char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
}
进行数据的写入一定要判断容量是否足够,移动字符时会发生整形提升造成死循环,要将size_t类型的pos强转成int类型 。移动结束后在pos位置插入字符,修改有效数据的个数。
pos位置插入字符串
void insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(_size + len);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str,len);
_size += len;
}
和尾插字符串差不多,但是在拷贝时从pos位置拷贝len个字符。最后修改有效数据的个数。
删除pos位置的n个字符
void erase(size_t pos, size_t len = npos)
{
if (len == npos || pos + len > _size)
{
_size = pos;
_str[_size] = '\0';
}
else
{
int begin = pos+len;
while (begin <= _size)
{
_str[begin-len] = _str[begin];
begin++;
}
_size = _size - len;
}
}
这是一个全缺省函数,当传入删除的长度时便表示删除从pos位置开始的所有数据。 就直接将有效数据修改为pos,将pos位置的值置为'\0'。
rsize
void rsize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);
while (_size < n)
{
_str[_size] = ch;
_size++;
}
_str[_size] = '\0';
}
}
半缺省函数,当n小于有效数据时相当于删除有效数据。当n大于有效数据时候先开辟空间在循环设置字符。
find(查找字符和查找子串)
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* sub, size_t pos = 0)
{
const char* p = strstr(_str + pos, sub);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
对于字符的查找使用循环遍历查找;对于字符串使用strstr库函数查找,在使用指针相加得到位置;
substr(获取子串)
string substr(size_t pos, size_t len = npos)
{
string s;
size_t end = pos + len;
if (len == npos || pos + len >= _size)
{
len = _size - pos;
end = _size;
}
s.reserve(len);
for (size_t i = pos; i < end; i++)
{
s += _str[i];
}
return s;
}
这里也要判断获取的长度,当len未输入值时候表示取到结尾。创建一个新的对象,将获取的每个字符 存到新的对象中,最后返回该对象。
clear(清除数据)
void clear()
{
_str[0] = '\0';
_size = 0;
}
将有效数据的个数设置成0即可,并不用释放空间;
其他的操作符重载
//+=重载 相当于尾插
String& operator +=(char ch)
{
push_back(ch);
return *this;
}
String& operator += (const char* str)
{
append(str);
return *this;
}
bool operator<(const String& s)const
{
return strcmp(_str, s._str) < 0;
}
bool operator==(const String& s)const
{
return strcmp(_str, s._str) == 0;
}
bool operator<=(const String& s)
{
return (*this < s) || (*this == s);
}
bool operator>(const String& s)
{
return !(*this <= s);
}
bool operator>=(const String& s)
{
return !(*this < s);
}
bool operator<(const String& s)
{
return !(*this >= s);
}
bool operator!=(const String& s)
{
return !(*this == s);
}
今天对string的底层模拟实现的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法。您三连的支持就是我前进的动力,感谢大家的支持!!!
相关文章:

【Linux系统化学习】进程的父子关系 | fork 进程
本篇文章介绍了进程的父子关系和使用fork函数创建一个进程!

【Linux系统化学习】探索进程的奥秘 | 第一个系统调用
本片文章主要介绍了Linux下的进程和第一个系统调用。

【C++干货铺】解密vector底层逻辑
本片文章主要是vector的介绍使用和手撕模拟实现!!!!

人工智能时代:AIGC的横空出世
AIGC是一种新的人工智能技术,即人工智能生成内容。它是一种基于机器学习和自然语言处理的技术,能够自动产生文本、图像、音频等多种类型的内容。

零基础搭建本地Nextcloud私有云结合内网穿透实现远程访问
本文主要讲解如何搭建本地Nextcloud私有云结合内网穿透实现远程访问

如何在外远程访问本地NAS威联通QNAP?
本文主要讲解在外远程访问本地NAS威联通QNAP。

使用Linux JumpServer堡垒机本地部署与远程访问
本文主要讲解如何使用Linux JumpServer堡垒机本地部署与远程访问。

Java中判断两个Long类型是否相等
也就是说这个值在-128到127之间会使用缓存,超过就会创建一个对象,所以上述的两个值分别创建了两个对象,那么使用。可以使用 .longValue() 或 .equals() 进行比较。推荐使用equals方法进行比较。现象1和现象2结果不一样,现象2使用==判断两个Long类型的值,结果竟然是false!

Nginx反向代理跳过国内备案(以宝塔面板为例)
Nginx代理跳过大陆备案验证,需要两台服务器,一台已备案或者免备案,一台国内主力服务器放你的项目。B服务器在配置文件里设置listen监听端口号。先把域名解析到A服务器。然后在A服务器里配置。

大华摄像头windows、linuxJavaSDK开发使用
本文档主要介绍 SDK 接口参考信息,包括主要功能、接口函数和回调函数。主要功能包括:SDK 初始化、设备登录、实时预览、云台控制、语音对讲、报警监听、智能订阅、录像回放和录像下载等。根据环境不同,开发包包含的文件会不同,具体如下所示。Windows 开发包所包含的文件如下:Linux 开发包所包含的文件如下:SDK 的功能库和配置库是必备库。功能库是设备网络 SDK 的主体,主要用于网络客户端与各类产品之间的通讯交互,负责远程控制、查询、配置及码流数据的获取和处理等。

什么是缓存穿透、缓存击穿、缓存雪崩,以及各自的解决方案
当缓存数据大面积失效,导致请求无法从缓存中拿到数据而是直接访问数据库。

并发编程的基本概念
单核 cpu 下,线程实际还是 串行执行 的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是 同时运行的。总结为一句话就是:微观串行,宏观并行,多核 cpu下,每个 核(core) 都可以调度运行线程,这时候线程可以是并行的。一般会将这种 线程轮流使用 CPU 的做法称为并发, concurrent。

try catch 应该在 for 循环里面还是外面?
有个老哥昨天被面试官欺负了,但是是被这个问题(标题)欺负的?其实是个比较基础的问题,只要有了解过,叙述是非常简单OK的。

储存容量单位:Bit, Byte, KB, MB, GB, TB , PB, EB, ZB, YB等的关系
有趣的是从 Wikipedia 看到的单位英文在「十进位」与「二进位」不同进制之间所使用的英文单字是不太一样的,例如我们常讲 30GB 会唸成 30 Gigabytes,不过正确的唸法应该是 Gibibytes 才对,不过大家都随便念、随便写,反正差不多、听的懂就好,我想唸过计算机概论的人自己都会知道 1GB = 1024 MB 吧,如果唸成 Gibibytes 搞不好还会被笑没知识!这样大的数据单位估计在未来的五年内是无法达到的,不过我相信假以时日人类的需求一定能够达到或者超越CB级。
Jmeter执行接口自动化测试-如何初始化清空旧数据
生命不息,奋斗不止。每一份努力都不会被辜负,只要坚持不懈,终究会有回报。珍惜时间,追求梦想。不忘初心,砥砺前行。你的未来,由你掌握!生命短暂,时间宝贵,我们无法预知未来会发生什么,但我们可以掌握当下。珍惜每一天,努力奋斗,让自己变得更加强大和优秀。坚定信念,执着追求,成功终将属于你!只有不断地挑战自己,才能不断地超越自己。坚持追求梦想,勇敢前行,你就会发现奋斗的过程是如此美好而值得。相信自己,你一定可以做到!

带有 RaspiCam 的 Raspberry Pi 监控和延时摄影摄像机
一段时间以来,我一直想构建一个运动激活且具有延时功能的树莓派相机,但从未真正找到我喜欢的案例。我在thingiverse上找到了这个适合树莓派和相机的好案例。它是为特定的鱼眼相机设计的,但从模型来看,我拥有的廉价中国鱼眼手机镜头之一似乎非常适合孔中。

Nginx基础篇:Nginx搭建、Nginx反向代理、文件服务器部署配置。
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,公开版本1.19.6发布于2020年12月15日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2022年01月25日,nginx 1.21.6发布。

如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发
如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发

使用Pytorch实现Grad-CAM并绘制热力图
就直接从官方的torch vision这个库当中导入一些我们常用的model。比如说我这里的例子是采用的mobile net v3 large这个模型。它就会自动的去下载torch官方在imagenet上预训练好的模型权重。我们这里采用的模型呢是在image net 1k上预训练好的模型。接着这里的target layers也要根据你自己的模型去设置。首先呢我们这里需要指定一下我们的target layers。紧接着呢我们还需要去指定一下我们所感兴趣的这个类别。

CentOS最小化安装后怎么转图形界面/可视化桌面?
2.如果在图形界面下,按:Ctrl+Alt+F2/F3,可以进入命令行模式。如果安装的是最小化,那么init 5 (进入图像化桌面)命令是无效的。1.如果在命令行模式,按Ctrl+Alt+F1,可以进入图形界面;设置完后,用上一条命令:systemctl get-default。注意:进入图形界面要创建一个新用户,同样要记好账号和密码。查看是否返回:Graphical.target。图形界面:终端输入init 3进入命令行。命令行:输入init 5进入图形。设置默认启动桌面(看个人喜欢)

Python中的深拷贝和浅拷贝的区别
深拷贝和浅拷贝是Python中非常重要的概念,它们在处理对象和数据结构时有着截然不同的行为。理解深拷贝和浅拷贝的区别以及适用场景对于面试和实际编程工作都非常有帮助。在选择深拷贝和浅拷贝时,需要根据具体的需求和数据结构来决定。

VMware安装Ubuntu20.04并使用Xshell连接虚拟机
注意,还原默认设置你的网络地址可能发生改变,而且之前如果手动配置过VMware8的IP地址和DNS服务器地址,也会还原为默认的自动获取IP地址和DNS服务器地址。如果你是新安装的VMware,你应该会直接看到下面还原了网络设置后的界面。根据下载链接,下载安装完成VMware,在VMware里创建虚拟机,镜像选择刚才下载的Ubuntu Server 20.04。注意,还原默认设置后,子网IP发生了变化,从。记住你配置的子网,后面配置的VMnet8、网关、虚拟机的IP地址都跟它有关。至于为什么选择这个版本?

Nginx按指定格式记录访问日志
其实我们在用常用的web服务器上都有这项功能,我们这里用Nginx举例,我们的访问日志一般正常都是什么设备在什么地址访问了我们的什么资源,后端服务器的响应时间是多少,客户端请求处理的总时间是多少;一般我们作为开发人员关注的日志只是在应用程序层面的,我们称它为应用程序日志,访问日志和错误日志可以被认为是应用程序日志的一部分,因为它们都与应用程序的运行状态和用户访问行为有关。:代表User-Agent HTTP头部,指示发起请求的客户端的用户代理(例如,浏览器)。:代表发起请求的客户端的IP地址。

Redis保证高可用的三种方式
Redis保证高可用主要有三种方式:主从、哨兵、集群。

库卡LBR_iisy_3_R760协作机器人导入到coppeliasim
一般载都是这个step文件格式,其他的好像不太好用。coppeliasim导入格式用的是stl,需要用freeCAD打开重新转换一下。下载下来后,很多都是一个整体,在freeCAD导入中,导入选择要不勾选合并。下载完用CAD Assisitant打开后是这个样子的。

nacos 2.0 版本在spring cloud 2022.0.0.0-RC2读取配置文件失败
报错信息如下Spring 官方给出的解决方案如下。

gRPC三种流和消息格式
服务端实现protocol buffer定义的方法,客户端保留一个存根,提供服务端方法的抽象,客户端只需要调用存根中的方法,就可以远程调用服务端方法。客户端多个请求发给服务端,服务端发送一个响应给客户端,比如更新业务,客户端的读个请求发过来,服务端更新完返回一个成功的结果。通信时可以是一个请求,服务端多次响应,比如查询业务,服务端模糊匹配找到一次就返回客户端一次响应这样的多次响应。客户端发送,包含3个部分:请求头信息、长度前缀的消息、流结束标记。在写入消息前,先写入长度消息表明每条消息的大小。

如何本地搭建Linux DataEase数据可视化分析工具并实现公网访问
如何本地搭建Linux DataEase数据可视化分析工具并实现公网访问

SpringBoot进行自然语言处理,利用Hanlp进行文本情感分析
自然语言处理,或简称NLP,是处理和转换文本的计算机科学学科。它由几个任务组成,这些任务从标记化开始,将文本分成单独的意义单位,应用句法和语义分析来生成抽象的知识表示,然后再次将该表示转换为文本,用于翻译、问答或对话等目的。

深入理解JVM内存空间的担保策略
本文将介绍 jvm中垃圾回收(GC)时空间担保策略是如何执行的