C++ string模拟实现
一 如何区分自定义类与标准库中的同名类
// string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
using namespace std;namespace bit
{class string{}
}// Test.cpp
include "string.h"int main()
{return 0;
}
既然要模拟实现string底层那就得先理解为什么我写的string和库string里面不会冲突。
1 头文件查找路径
当我们用双引号 "" 包围头文件时,编译器会首先在当前目录下查找这个头文件。这意味着它会优先找到我们自己定义的头文件(比如 string.h),而不是标准库中的头文件。因为标准库的头文件通常在其他系统目录中,所以不会发生冲突。
2 避免名字冲突
虽然使用双引号包含头文件可以让编译器优先使用我们自定义的头文件,但如果我们在头文件中定义了一个与标准库相同名称的类或函数,仍然可能导致混淆或冲突。
为了解决这个问题,我们可以使用命名空间。命名空间就像是一个独立的区域,把我们的代码和标准库的代码隔离开来。比如,我们创建一个 bit 命名空间,然后在这个命名空间里定义一个与标准库同名的类或函数,这样就可以避免冲突。
当我们在代码中使用这些定义时,需要明确指明是哪个命名空间下的。比如,使用 bit::string 来表示我们自定义的 string 类,而 std::string 则表示标准库中的 string 类。这样编译器就能清楚地区分它们。
二 构造 / 拷贝 / 析构函数
2.1 构造
2.1.1 空字符串构造函数(默认构造函数)
string():_str(new char[1]),_size(0),capacity(0)
{_str[0] = '\0';
}
在std::string类,空字符串的初始化会创建一个包含单个字符('\0')这个字符用于表示字符串的结束。
那么:_str(new char[1])就是在堆上new一个包含1个char元素的数组。然后给这唯一的元素赋值'\0'(赋值运算都是在构造函数体内进行)。
2.1.2常量字符串构造函数
string(const char* str)_str(new char[strlen(str)+1])_size(strlen(str))_capasize(strlen(str)){strcpy(_str , str);}
既然要把常量字符串复制过来那么首先要知道它的长度(strlen(str)计算长度不包过'\0'所以要+1),然后让成员变量_str指向堆上新new的空间,空间有了接下俩就可以把字符串复制过来了,而C语言中strcpy函数就是一个很好的选择。从上面代码可以看出来我们多次调用strlen函数来计算字符串长度,可不可以只使用一次,并且将有值构造和默认构造合二唯一?
2.1.3 合并
string(const char* str = " ")_size(strlen(str))
{_str = new char[_size + 1];_capacity = _size;strcpy(_str , str);
}
合并之后它可以同时作为默认构造函数和常量字符串构造函数。我们首先在初始化列表中确定了字符串的长度并初始化了大小和容量,然后在构造函数体中分配适当大小的内存并将字符串复制到新内存中。
2.2 拷贝构造(深拷贝)
// 深拷贝
string(const string& s)
{_str = new char [s.capacity + 1];_size = s._size;_capacity = s._capacity;strcpy(_str , s._str)
}// 浅拷贝
string(const string& s)
{_str = s._str;// 直接复制指针_size = s._size;_capacity = s._capacity;}
回顾一下深拷贝与浅拷贝基本知识内容就基本上懂为什么要写深拷贝而不是以往的浅拷贝
深拷贝:
复制对象时,分配新的内存,并复制指针指向的内容。结果是两个对象各自拥有独立的内存,不会互相影响。
浅拷贝:
复制对象时,仅复制指针,而不复制指针指向的内容。结果是两个对象共享同一块内存,这可能导致一个对象修改数据,另一个对象的数据也被修改,甚至导致双重释放问题(两个对象在析构时都试图释放同一块内存)。
2.3 析构
~string()
{delete[] _str;_str = nullptr;size = 0;capacity = 0;}
讲完了如何初始化那就该讲讲怎么遍历了
三 遍历字符串
遍历字符串有operator[ ] 、迭代器 俩种方式,那现在让我们模拟实现它们吧。
3.1 operator[ ]
重载operator[]可以让我们像访问数组元素一样访问类的成员变量。
char& operator[](size_t pos)
{assert(pos < _size);return _size[pos]
}
3.2 迭代器
我们一般说迭代器类似指针但是却不是指针
typedef char* iterator;iterator begin()
{return _str;
}iterator end()
{return _str + _size;
}
四 对内容进行修改
4.1 reserve
// 开空间void reserve(size_t n)
{if(n > capacity){char* tmp = new char[n + 1];strcpy(str , tmp);delete[] _str;_str = tmp;capacity = n;}
}
4.2 push_back
// 尾部添加单个字符void push_back(char ch)
{ if(_size == capacity){reserve(capacity == 0 ? 4: capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0'
}
4.3 append
// 尾部添加一个字符串void append(const char* str)
{size_t len = strlen(str);if(len + _size > capacity){reserve(len + _size);}strncpy(_str + _size , str , len);_size += len;
}
operator+=
// 尾插单个字符版string& operator+=(char ch)
{ push_back(ch);return *this;
}// 尾插字符串版string& operator+=(const char* str)
{ append(str);return *this;
}//尾插字符串测试代码int main()
{String str;str += "Hello, " += "world!";
return 0;
}
当执行 str += "Hello, "; 时,编译器会识别出这是 operator+= 操作,并且调用我们为 String 类定义的 operator+= 函数。
在 operator+= 函数内部,this 是一个指向 str 对象的指针。所以在函数内部,this-> 和 str. 是等价的。
在 operator+= 函数中,调用了 append(str),其中 str 是传入的 C 字符串 "Hello, "。此时,this 仍然指向 str 对象。
operator+= 返回当前对象的引用 *this,即 str的引用。
4.4 insert
//在指定位置插入字符void insert(size_t pos, char ch)
{assert(pos <= _size);if(pos == _capacity){reserve(capacity == 0 ? 4: capacity * 2);}size_t end = _sizewhile(end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;_size += 1;
}//在指定位置插入字符串void insert(size_t pos, const char* ch)
{assert(pos <= _size);size_t len = strlen(ch);if(_size + len >= _capacity){reserve((_size + len) *2);}size_t end = _size;while(end >= pos){ _str[end + _size] = _str[end];--end;}strncpy(_str + pos , ch , len);_size += len;
}
4.5 erase
// 从指定位置删除指定长度的字符void erase(size_t pos, size_t len = npos)
{ assert(pos < _size);if(len + pos > _size || len == npos){ _str[pos] = '\0';_size = pos;}strcpy(_str + pos , _str + pos + len);_size -= len;
}
当 pos + len 超过了有效数据的总长度时,说明操作已经超出范围,这时候最简单的做法就是从 pos 开始删除后面的所有内容。反之,如果 len 没有超出范围,那么就不需要做任何删除操作。
- 如果没有显式传递
len参数,它默认会使用npos作为len的值。npos通常表示一个无效位置或者未找到的值,通常等同于-1(但在size_t类型中,-1实际上是一个非常大的正整数)。 - 当
len == npos时,意味着你没有指定要删除或操作的长度。在这种情况下,函数通常会理解为你希望操作直到字符串的末尾。
4.6 find
// 查找字符size_t find(char ch,size_t pos=0) const
{ assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;
}// 查找字符串// const 在函数签名后面:表示这个成员函数不能修改所属对象的状态。
size_t find(const char* str, size_t pos = 0) const
{ assert(pos < _size);// 用于查找一个字符串在另一个字符串中的首次出现位置// 找到返回它在 _str + pos 中首次出现的位置;如果未找到,a 将为 nullptr。const char* m = strstr(_str + pos, str);if(a){ return m - _str; //指针减去指针返回它们相差的个数 }else{ return nops; }
}
4.7 substr
// 在原字符串中获取子串string substr(size_t pos = 0, size_t len = npos)
{string Tmp;assert(pos < _size);if(pos + len > _size || len == pos){for(int i = pos; i < _size; i++){// 字符被添加到了 sub 对象所指向的字符串存储空间中,而这个存储空间的指针是 sub 对象的一部分。Tmp += _str[i];}else{for(int i = pos; i < len + pos; i++){sub += _str[i];}}}return Tmp;
}
五 重载函数
5.1 operator=
/ 赋值重载string& operator=(const string& s)
{ char* Tmp = new char[strlen(s._capacity) + 1];strcpy(Tmp , _str);delete[] _str;_size = s._size;_capacity = s._capacity;return *this;
}
5.2 operator==
// 等几个比较函数bool operator==(const string& s1, const string& s2)
{ int m = strcmp(s1._str , s2._str);return m == 0;
}bool operator<(const string& s1, const string& s2)
{int x = strcmp(s1._str , s2._str);return x < 0;
}
5.3 operator<<
// 流插入与提取ostream& operator<<(ostream&out,const string&s)
{for(auto e : s){out << ch;}return out
}相关文章:
C++ string模拟实现
一 如何区分自定义类与标准库中的同名类 // string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include<iostream> using namespace std;namespace bit {class string{} }// Test.cpp include "string.h"int main() {return 0; } 既然要模拟实现str…...
Lora 全文翻译
作者: 地点:hby 来源:https://arxiv.org/pdf/2106.09685 工具:文心 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS 摘要 自然语言处理的一个重要范式包括在通用领域数据上进行大规模预训练,并适应特定任务或…...
结题阶段(2024年8月)
海门区教育科学 “十四五”规划2022年度立项课题 结题鉴定材料 课 题 名 称 高中信息技术项目化教学的研究与应用 课题负责人 郭书艳 所 在 单 位 江苏省包场高级中学 报 送 日 期 2024 年 6 月 20 日…...
贪吃蛇(C语言详解)
贪吃蛇游戏运行画面-CSDN直播 目录 贪吃蛇游戏运行画面-CSDN直播 1. 实验目标 2. Win32 API介绍 2.1 Win32 API 2.2 控制台程序(Console) 2.3 控制台屏幕上的坐标COORD 2.4 GetStdHandle 2.5 GetConsoleCursorlnfo 2.5.1 CONSOLE_CURSOR_INFO …...
国际以太网专线(IEPL)与国际专线(IPLC)服务
中国联通国际公司产品: 国际以太网专线 (IEPL)/国际专线(IPLC) 在全球化的今天,企业越来越依赖于高速、稳定且安全的国际网络连接来支持其跨国业务活动。中国联通国际公司作为中国领先的电信运营商之一,在这一领域提供了多种优质…...
vue 子父组件互相改值
在Vue.js中,子组件想要修改父组件的状态(如数据属性的值)时,通常遵循以下步骤: 父组件向子组件传递数据:通过props(属性)将需要被子组件操作的值传入子组件。例如,在父组…...
java之拼图小游戏(开源)
public class LoginJFrame extends JFrame {//表示登录界面,以后所有跟登录相关的都写在这里public LoginJFrame() {//设置界面的长和宽this.setSize(603,680);//设置界面的标题this.setTitle("拼图登陆界面");//设置界面置顶this.setAlwaysOnTop(true);/…...
Linux Shell批量测试IP连通性
Linux 通过Shell脚本来实现读取txt文件中的IP地址,并使用telnet对其后的所有端口进行测试,判断是否可以连接。每个IP地址的端口测试时间限制为5秒。 IP文件 : ips.txt 192.168.1.1 22,80,443 192.168.1.2 21,25,110 192.168.1.3 8080每一行包含一个IP地…...
已解决:anaocnda如何备份环境与安装环境
1.使用pip进行备份 激活对应的虚拟环境,切换到桌面或者想备份的位置。 备份即可: pip freeze > requirements.txt如何安装备份? pip install -r requirements.txt2.使用conda进行备份 激活对应的虚拟环境,切换到桌面或者想…...
自动化与高效设计:推理技术在FPGA中的应用
想象一下,你正在设计一个复杂的电路系统,就像在搭建一座精巧的积木城堡。你手头有各种形状和功能的积木块,这些积木块可以组合成任何你需要的结构。在这个过程中,你有两种主要的方法:一种是手动挑选和搭建每一块积木&a…...
对react模块和模块化理解
在React开发中,模块化和React模块是两个紧密相关但又有区别的概念。理解它们对于构建高效、可维护的React应用至关重要。 模块化 模块化是一种将大型代码库拆分成更小、更易于管理的部分(即模块)的软件设计技术。每个模块都封装了特定的功能…...
CAN总线-----帧格式
目录 前言 一、CAN总线帧格式分类 1.数据帧(重点) 2.遥控帧 3.错误帧 4.过载帧 5.间隔帧 二、位填充 三、波形实例 前言 本期我们就开始学习CAN总线的帧格式,对应帧格式的话,在前面我们学习I2C协议和SPI协议等协议的时候…...
UE网络同步(一) —— 一个项目入门UE网络同步之概念解释
最近在学习UE网络同步,发现了一个非常好的教程,并且附带了项目文件,这里从这个小项目入手,理解UE的网络同步 教程链接:https://www.youtube.com/watch?vJOJP0CvpB8w 项目链接:https://github.com/awforsyt…...
MATLAB中rsf2csf函数用法
目录 语法 说明 示例 将实数 Schur 形式变换为复数 Schur 形式 rsf2csf函数的功能是将实数 Schur 形式转换为复数 Schur 形式。 语法 [Unew,Tnew] rsf2csf(U,T) 说明 [Unew,Tnew] rsf2csf(U,T) 将实矩阵 X 的 [U,T] schur(X) 的输出从实数 Schur 形式变换为复数 Sc…...
Java基础 文字小游戏
souf System.out.printf("你好啊%s","张三") 输出你好啊张三 System.out.printn()放在中间可以换行 System.out.printf("%s你好啊%s","张三","李四") 输出 张三你好啊李四 只有输出没有换行效果。 制作一个文字小游戏…...
「数组」归并排序 / if语句优化|小区间插入优化(C++)
概述 在上一篇文章中,我们介绍了快速排序以及随机快速排序: 「数组」快速排序 / 随机值优化|小区间插入优化(C) 今天,我们来介绍归并排序。 相比于快速排序是冒泡排序融合了分治思想后形成的究极promax进化版&…...
颠覆传统 北大新型MoM架构挑战Transformer模型,显著提升计算效率
挑战传统的Transformer模型设计 在深度学习和自然语言处理领域,Transformer模型已经成为一种标准的架构,广泛应用于各种任务中。传统的Transformer模型依赖于一个固定的、按深度排序的层次结构,每一层的输出都作为下一层的输入。这种设计虽然…...
接口优化笔记
索引 添加索引 where条件的关键自动或者order by后面的排序字段可以添加索引加速查询 索引只能通过删除新增进行修改,无法直接修改。 # 查看表的索引 show index from table_name; show create table table_name; # 添加索引 alter table table_name add index …...
pandas 科学计数法显示
我注意到pandas中有一个问题, 默认情况下,就是其中的数据的小数位不能超过6位,比如0.0000007就会被显示为0,这个结果如下 全部以科学技术显示 import pandas as pd import numpy as np# 设置显示格式为科学计数法 pd.options.d…...
PHP正则替换字符串中的图片地址
在PHP中,可以使用preg_replace()函数来实现正则表达式的替换功能。以下是一个简单的例子,演示如何替换字符串中的图片地址。 double $str 图片地址1:<img src"http://example.com/image1.jpg"> 图片地址2:<i…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
OCC笔记:TDF_Label中有多个相同类型属性
注:OCCT版本:7.9.1 TDF_Label中有多个相同类型的属性的方案 OCAF imposes the restriction that only one attribute type may be allocated to one label. It is necessary to take into account the design of the application data tree. For exampl…...
DJango知识-模型类
一.项目创建 在想要将项目创键的目录下,输入cmd (进入命令提示符)在cmd中输入:Django-admin startproject 项目名称 (创建项目)cd 项目名称 (进入项目)Django-admin startapp 程序名称 (创建程序)python manage.py runserver 8080 (运行程序)将弹出的网址复制到浏览器中…...
ai流式文字返回前端和php的处理办法
PHP后端 php端主要是用到ob_flush和flush,头改为流式。 基本代码 代码如下: <?php header(Content-Type:text/event-stream); header(Cache-Control:no-cache); header(Connection:keep-alive);function streamPostRequest($url,$data){$chcurl_…...
