C++String类
1. 前言
String是C++中操作字符串的类,它是在比较早的时候设计的STL模板,因此在某些地方设计的有些冗余
对于String类,不仅仅是学会使用它,更重要的是要从底层去理解它;本篇文章将从底层出发,模拟实现常用的String类接口(实现方式与不同平台下的标准库中的实现不一定相同)
2. 接口
2.1 成员函数
// 构造
string(const char* str = "");// 拷贝构造
string(const string& s);// 赋值运算符重载
string& operator=(const string& s);// 析构
~string();
2.2 迭代器
typedef char* iterator;
typedef const char* const_iterator;// 可读可写
iterator begin();
iterator end();// 只读
const_iterator begin() const;
const_iterator end() const;
2.3 容量
// 元素个数
size_t size() const;// 当前容量
size_t capacity() const;// 当 n < size 时,缩容,不改变capacity
// 当 size <= n <= capacity 时,什么都不做
// 当 size >= capacity 时,扩容,用 ch 补剩余元素
void resize(size_t n, char ch = '\0');// 当 n <= capacity 时,不改变capacity
// 当 n > capacity 时,扩容
void reserve(size_t n);
2.4 修改
// 尾插
void push_back(char ch);// 追加一个字符/字符串
string& operator+=(char ch);
string& operator+=(const char* str);// 追加字符串
void append(const char* str);// 清空字符串(不改变容量)
void clear();// 在pos位置插入一个字符/字符串
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);// 在pos位置删除len个字符
void erase(size_t pos, size_t len = npos);// 交换
void swap(string& s);
2.5 元素访问
char& operator[](size_t pos);
const char& operator[](size_t pos) const;
2.6 字符串操作
// 获取字符串
const char* c_str() const;// 从pos位置开支查找一个字符/字符串
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str, size_t pos = 0) const;// 获取字串
string substr(size_t pos = 0, size_t len = npos) const;
2.7 非静态成员函数重载
// 比较大小
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);// 流插入/提取运算符重载
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);// 交换
void swap(string& s1, string& s2);// 获取一行数据
void getline(istream& in, string& s);
3. 模拟实现
namespace byh
{class string{public://constructorstring(const char* str = "");//copy constructorstring(const string& s);//assignment operator overloadingstring& operator=(const string& s);//destructor~string();//iteratortypedef char* iterator;typedef const char* const_iterator;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;//capacitysize_t size() const;size_t capacity() const;void resize(size_t n, char ch = '\0');void reserve(size_t n);//modifyvoid push_back(char ch);string& operator+=(char ch);string& operator+=(const char* str);void append(const char* str);void clear();void insert(size_t pos, char ch);void insert(size_t pos, const char* str);void erase(size_t pos, size_t len = npos);void swap(string& s);//accesschar& operator[](size_t pos);const char& operator[](size_t pos) const;//string operationsconst char* c_str() const;size_t find(char ch, size_t pos = 0) const;size_t find(const char* str, size_t pos = 0) const;string substr(size_t pos = 0, size_t len = npos) const;private:char* _str;size_t _size;size_t _capacity;static const size_t npos;};const size_t string::npos = -1;//Non-member function overloadbool operator==(const string& s1, const string& s2);bool operator!=(const string& s1, const string& s2);bool operator>(const string& s1, const string& s2);bool operator>=(const string& s1, const string& s2);bool operator<(const string& s1, const string& s2);bool operator<=(const string& s1, const string& s2);ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);void swap(string& s1, string& s2);void getline(istream& in, string& s);
}
#include<iostream>
#include<assert.h>
using namespace std;namespace byh
{class string{public:static const size_t npos;//constructorstring(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}//copy constructorstring(const string& s){string temp(s.c_str());swap(temp);}//assignment operator overloadingstring& operator=(string s){swap(s);return *this;}//destructor~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//iteratortypedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//capacitysize_t size() const{return _size;}size_t capacity() const{return _capacity;}void resize(size_t n, char ch = '\0'){if (n < _size){_str[n] = '\0';_size = n;}else{reserve(n);for (int i = _size; i < n; i++){_str[i] = ch;}_str[n] = '\0';_size = n;}}void reserve(size_t n){if (n > _capacity){char* temp = new char[n + 1];strcpy(temp, _str);_str = temp;_capacity = n;}}//modifyvoid push_back(char ch){insert(_size, ch);}string& operator+=(char ch){insert(_size, ch);return *this;}string& operator+=(const char* str){insert(_size, str);return *this;}void append(const char* str){insert(_size, str);}void clear(){_str[0] = '\0';_size = 0;}void insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity)reserve(_capacity == 0 ? 4 : 2*_capacity);size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size += 1;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity)reserve(_size + len);size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;}void erase(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || len >= _size - pos){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}//swap(s1,s2)void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//accesschar& operator[](size_t pos){return _str[pos];}const char& operator[](size_t pos) const{return _str[pos];}//string operationsconst char* c_str() const{return _str;}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;}size_t find(const char* str, size_t pos = 0) const{assert(pos < _size);char* temp = strstr(_str + pos, str);if (temp)return (temp - _str);elsereturn npos;}string substr(size_t pos = 0, size_t len = npos) const{assert(pos < _size);string temp;if (len == npos || _size - pos <= len){for (size_t i = pos; i < _size; i++){temp += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){temp += _str[i];}}return temp;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};const size_t string::npos = -1;//Non-member function overloadbool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}bool operator>(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) > 0;}bool operator>=(const string& s1, const string& s2){return (s1 > s2 || s1 == s2);}bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator<=(const string& s1, const string& s2){return !(s1 > s2);}ostream& operator<<(ostream& out, const string& s){out << s.c_str();return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = 0;char temp[128] = { 0 };int i = 0;ch = in.get();while (ch != ' ' && ch != '\n'){temp[i++] = ch;if (127 == i){temp[i] == '\0';s += temp;i = 0;}ch = in.get();}if (i > 0){temp[i] == '\0';s += temp;}return in;}void swap(string& s1, string& s2){s1.swap(s2);}void getline(istream& in, string& s){s.clear();char ch = 0;char temp[128] = { 0 };int i = 0;ch = in.get();while (ch != '\n'){temp[i++] = ch;if (127 == i){temp[i] == '\0';s += temp;i = 0;}ch = in.get();}if (i > 0){temp[i] == '\0';s += temp;}}
}
4. string的额外知识
string中的扩容机制在不同平台下是不同的
- VS:第一次1.5倍扩容,之后都是2倍扩容
- g++:2倍扩容
void Test_increase_capacity() {string s;size_t sz = s.capacity();cout << "capacity->" << sz << endl;for (int i = 0; i < 100; i++){s.push_back('1');if (sz != s.capacity()){sz = s.capacity();cout << "capacity->" << sz << endl;}} }// VS // capacity->15 // capacity->31 // capacity->47 // capacity->70 // capacity->105// g++ //capacity->0 //capacity->1 //capacity->2 //capacity->4 //capacity->8 //capacity->16 //capacity->32 //capacity->64 //capacity->128造成扩容机制不同的本质原因是VS和g++中string的结构不同
cout << sizeof(string) << endl;// VS:28 // g++:8
VS下string的结构
先是有一个联合体,里面定义了一个长度为16的数组和一个指针
当字符串的长度 <= 16时,使用内部的数组存储字符串
当字符串长度 >16 时,使用指针开辟空间
由于大部分的字符串长度是小于16的,因此直接在栈上开了空间,比去堆上开空间效率高
其次,有一个size_t类型的数据用来表示字符串长度,一个size_t类型的数据用来表示容量
再有一个指针用来干其他事
g++下的string结构
g++下string是用写时拷贝实现的,内部只包含一个指针,该指针指向一块堆空间,里面包含
字符串长度
容量
引用计数
一个指针,用来存放字符串
写时拷贝
当拷贝构造/赋值时,不是开辟新的空间,而是让构造的对象/赋值的对象指向原本的空间,达到节省空间的效果
这样做的问题时:
- 同一块空间会被析构多次
- 修改其中一个对象会影响其他对象
针对问题1的解决方案:当一个对象被析构时,让引用计数count-1,只有当count为0时,再去释放空间
针对问题2的解决方案:当一个对象要被修改时,检查count,如果为1,说明这块空间是该对象独占的,可以任意修改;如果大于1,拷贝新的空间给该对象,count-1
相关文章:
C++String类
1. 前言 String是C中操作字符串的类,它是在比较早的时候设计的STL模板,因此在某些地方设计的有些冗余 对于String类,不仅仅是学会使用它,更重要的是要从底层去理解它;本篇文章将从底层出发,模拟实现常用的S…...
Linux docker7--私有镜像仓库registry和UI搭建及使用
一、对于开源的镜像,如redis,nginx等,可以通过官方仓库Docker Hub,或者国内的阿里云等共有仓库下载获取到镜像。但是企业内对于自己的研发产品不可能往公共仓库去发布镜像的,一般都会搭建私有的镜像仓库,保…...
IDS入侵检测系统分为两大类。
一、基于签名的IDS和基于异常的IDS。 基于签名的Ids主要依赖于已知的攻击模式库来检测入侵行为,适用于检测已知的攻击模式。 基于异常的Ids则关注网络流量的行为特征,通过分析数据包之间的关系和统计模型来判断是否存在异常行为,更适用于检…...
为什么元素显示的样式跟我设置的不一样?CSS优先级详解
一、什么是CSS中的选择器优先级? 在CSS中,选择器优先级是指确定应用于元素的最相关CSS声明的算法。这个优先级决定了哪个样式规则将被应用到元素上。根据选择器的类型和特定性,CSS规定了不同的优先级,例如ID选择器比类选择器具有更…...
C语言动态内存的管理
前言 本篇博客就来探讨一下动态内存,说到内存,我们以前开辟空间大小都是固定的,不能调整这个空间大小,于是就有动态内存,可以让我们自己选择开辟多少空间,更加方便,让我们一起来看看动态内存的有…...
CASIA数据集转png HWDB2.0-2.2
https://nlpr.ia.ac.cn/databases/handwriting/Home.html CASIA在线和离线中文手写数据库 https://nlpr.ia.ac.cn/databases/handwriting/Offline_database.html CASIA-HWDB2.0-2.2 离线文本数据库是由孤立字符数据集的作者制作的。每人撰写了五页给定文本。由于数据丢失&a…...
学习或复习电路的game推荐:nandgame(NAND与非门游戏)、Turing_Complete(图灵完备)
https://www.nandgame.com/ 免费 https://store.steampowered.com/app/1444480/Turing_Complete/ 收费,70元。据说可以导出 Verilog !...
前端面试题《react》
说说React render方法的原理?在什么时候会被触发? render函数里面可以编写JSX,转化成createElement这种形式,用于生成虚拟DOM,最终转化成真实DOM 在 React 中,类组件只要执行了 setState 方法,…...
快速入门Kotlin③类与对象
类 构造函数 主构造函数:主构造函数是类头的一部分,它跟在类名后面。主构造函数没有函数体,它可以包含初始化代码和属性声明。初始化块:init关键字修饰,它直接写在类体中。它的执行顺序与它们在类体中的出现顺序一致。 次构造函数:次要构造函数是可选的,用于提供额外…...
RUST:Arc (Atomic Reference Counted) 原子引用计数
在Rust编程语言中,Arc 是一个智能指针类型,全称为 "Atomic Reference Counted"(原子引用计数)。它的主要作用是提供线程安全的共享所有权机制,使得多个线程可以同时持有同一个数据结构的访问权,并…...
从0写一个问卷调查APP的第13天-1
1.今日任务 我也只是一个大学生,有什么思路不对的地方给我指出来哟! 分析:上次我们实现了任务调查的插入。但是我们插入的问卷调查只有它的标题,也就是这个问卷调查是什么我们告诉数据库了,但是现在我们还没有给它添加任何问题&…...
20.Python从入门到精通—参数 位置参数 关键字参数 默认参数 匿名函数 return 语句 强制位置参数
20.从入门到精通:参数 位置参数 关键字参数 默认参数 匿名函数 return 语句 强制位置参数 参数位置参数关键字参数默认参数 匿名函数return 语句强制位置参数 参数 在Python中,函数可以接受任意数量的参数,包括位置参数、关键字参数和默认参数。以下是这…...
Python爬虫之requests库
1、准备工作 pip install requests 2、实例 urllib库中的urlopen方法实际上就是以GET方式请求网页,requests库中相应的方法就是get方法。 import requestsr requests.get(https://www.baidu.com/) print(type(r)) # <class requests.models.Response> 响…...
鱼塘钓鱼(多路归并)
有 N 个鱼塘排成一排,每个鱼塘中有一定数量的鱼,例如:N5 时,如下表: 鱼塘编号12345第1分钟能钓到的鱼的数量 (1…1000)101420169每钓鱼1分钟钓鱼数的减少量(1…100)24653当前鱼塘到…...
java每日一题——买啤酒(递归经典问题)
前言: 非常喜欢的一道题,经典中的经典。打好基础,daydayup!!!啤酒问题:一瓶啤酒2元,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元可以喝几瓶 题目如下: 啤酒问题:一瓶…...
最近接到一个大项目,给公司设计抢商品代金劵业务
我们公司是做汽车金融方面的工作,在业内还挺大。目前单量来源于2,3线城市,随着大环境越老越差位了吸引他们, 公司决定给全国的客户,销售等发一些商品 1.总体学习了京东开源秒杀系统设计思路和方案。 我们公司决定进行…...
防火墙(讲解)
目录 1.防火墙是什么? 2.防火墙设备 3.防火墙功能 1)出色的控制能力,过滤掉不安全服务 2)过滤非法用户和访问特殊站点 3)它能够对网络存取和访问进行监控审计 4.防火墙的局限 (1)防火墙有可能是可以绕过的 (2)防火墙不能防止内部出卖性攻击或者内部误操作…...
Python之装饰器-带参装饰器
Python之装饰器-带参装饰器 带参装饰器 之后不是一个单独的标识符,是一个函数调用函数调用的返回值又是一个函数,此函数是一个无参装饰器带参装饰器,可以有任意个参数 func()func(1)func(1, 2) def add(x, y):"""函数说明&…...
抖音IP属地怎么更改
抖音是一个非常受欢迎的短视频平台,吸引了无数用户在上面分享自己的生活和才艺。然而,随着快手的火爆,一些用户开始担心自己的IP地址会被他人获取,引起个人隐私风险。那么,抖音用户又该如何更改到别的地方呢࿱…...
Flutter 全局控制底部导航栏和自定义导航栏的方法
1. 介绍 导航栏在移动应用中扮演着至关重要的角色,它是用户与应用之间进行导航和交互的核心组件之一。无论是简单的页面切换,还是复杂的应用导航,导航栏都能够帮助用户快速找到所需内容,提升用户体验和应用的易用性。 在移动应用…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...

