string类详解及重要函数实现

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

         今日主菜:string类

                 主厨:邪王真眼

            所属专栏:c++专栏

              主厨的主页:Chef‘s blog

前言:

咱们之前也是打开了c++的大门,相信很多朋友对这门新的语言都充满了好奇,那么今天就来份硬菜满足一下各位,铛铛铛铛,c++第二座大山:STL之string,他来了!

【本节目标】

1. 为什么要学习string

2. 标准库中的string

3. string类的模拟实现

1. 为什么学习string类?

1.1 C语言中的字符串

C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数,
但是这些库函数与字符串是分离开的,不太符合 OOP(面向对象) 的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

1.2 编程题解答

OJ 中,有关字符串的题目基本以 string 类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string 类,很少有人去使用 C 库中的字符串操作函数。

2. 标准库中的string

2.1 string类是什么

  • 1. 他是一个成员变量包含字符指针的类,有多种与字符指针有关的的接口,可以对他进行多种操作
  • 2.在使用string类时,必须包含#include<string>以及using namespace std;

2.2string的构造函数

2.2.1介绍

void Teststring()
{string s1; // 构造空的string类对象s1string s2("hello"); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3
}

2.2.2代码实现:

我们基本不用第三种,所以这里只实现 124,我们把12通过缺省参数来结合

class string
{
public:string(const char* str = "")//"\0"=="",因为结尾没有'\0'会补: _size(strlen(str)), _capacity(_size == 0 ? 3 : _size){_str = new char[_capacity + 1];//_capacity不包括最后的'\0'strcpy(_str, str);}string(const string& s):_size(s._size), _capacity(_size==0?3:_size){_str = new char[_size + 1];strcpy(_str, s._str);}private:char* _str;size_t _size;//大小不包括'\0',表示这个字符指针所容纳的字符数量size_t _capacity;//大小不包括'\0',表示字符指针指向的空间的大小,即开辟的空间大小
};

2.3 string类对象的容量操作

2.3.1介绍

注意:
  • 1. size()length()方法底层实现原理完全相同,因为string的出现是早于STL的,一开始也只有length,但后来在STL中别的容器(例如队列,链表,树)用的都是size,为了保证接口一致,于是又给string设计了size接口,一般情况下基本都是用size()
  • 2. clear()只是将string中有效字符清空,不改变底层空间大小。
  • 3. reserve:为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。
  • 4. resize(size_t n) resize(size_t n, char c) 都是将字符串中有效字符个数改变到 n 个,不同的是当字符个数增多时:resize(n) 0 来填充多出的元素空间, resize(size_t n, char c) 用字符 c 来填充多出的元素空间。注意:resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。我们也是利用缺省参数将二者合并。

2.3.2代码实现

class string{public:size_t size()const//length的实现与它完全一样,就是名字不一样{return _size;}size_t capacity()const{return _capacity;}bool empty(){return _size == 0;}void clear(){strcpy(_str, "");_size = 0;}void reserve(size_t a){if (a > _capacity)//扩容,那就要重新开辟一份空间{char* str = new char[a + 1];strcpy(str, _str);//将原本的数拷贝到开的空间delete[] _str;//释放原来的空间,防止内存泄漏_str = str;_capacity = a;//更改所记录的容量值}}void resize(size_t a,char ch='\0'){if (a > _capacity){char* str = new char[a + 1];//记得多开一个给'\0'strcpy(str, _str);delete[] _str;_str = str;while (_size != a)//将多余的空间进行赋初值{_str[_size] = ch;}_str[_size] = '\0';_capacity = a;}else{_size = a;_str[_size] = '\0';//不缩容,只要改记录的_size就好了}}private:char* _str;size_t _size;//大小不包括'\0',表示这个字符指针所容纳的字符数量size_t _capacity;//大小不包括'\0',表示字符指针指向的空间的大小,即开辟的空间大小};

2.4. string类对象的访问及遍历操作

2.4.1介绍

2.4.2代码实现 

class string
{
public:char& operator[](size_t pos)//重载一下,对应传参const和非const{assert(pos < _size);//先判断有没有越界return _str[pos];直接返回所求的下标的值,记住是传引用,因为可能需要修改}const char& operator[](size_t pos)const{assert(pos < _size);return _str[pos];}typedef char* iterator;//我们命名了两种迭代器,用于后面对于const和非const的返回值类型typedef const char* const_iterator;对于string类,所谓迭代器就是char*指针iterator begin()//返回存储字符开始的空间的指针{return _str;}iterator end(){return _str + _size返回最后指向'\0'的指针;}const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}private:char* _str;size_t _size;//大小不包括'\0',表示这个字符指针所容纳的字符数量size_t _capacity;//大小不包括'\0',表示字符指针指向的空间的大小,即开辟的空间大小
};

2.4.3范围for:

范围for的实现就是依靠迭代器:看代码

for (auto& c : d)//不能out<<_str,因为可能中间有'\0'out << c;
for (string::iterator it = d.begin(); it < d.end(); it++)cout << *it;

这两个循环是完全等价的,事实上,编译器就是会在编译时把上面的循环转化为下面的迭代器循环,证据就是范围for的反汇编代码:

2.5. string类对象的修改操作

2.5.1介绍

注意:
  • 1. string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  • 2. string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

2.5.2代码实现:

class string
{
public:string& operator+=(const char* str){this->append(str);//与append作用相同,所以直接调用函数return *this;}string& operator+=(const char ch)//与push_back作用相同,所以直接调用函数{this->push_back(ch);return *this;}string& operator+=(const string& s){this->append(s._str);//与append作用相同,所以直接调用函数return *this;}void push_back(const char ch){if (_size + 1 > _capacity)//就扩容{reserve(_capacity * 1.5);//1.5倍扩容}_str[_size] = ch;_size++;_str[_size] = '\0';//结尾是'\0'别忘了}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity)//就扩容{reserve(_capacity + len);}strcpy(_str + _size, str);_size += len;//——capacity已经在reserve中调整过了}size_t find(const char ch, size_t pos = 0)//从下标为pos出开始查找,看有没有目标字符{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){assert(pos < _size);char* p = strstr(_str + pos, str);从下标为pos出开始查找,看有没有目标字符串if (p == nullptr)return npos;//npos的值就是无符号数的-1,也就是42亿多return p - _str;}const char* c_str(){return _str;}
private:char* _str;size_t _size;//大小不包括'\0',表示这个字符指针所容纳的字符数量size_t _capacity;//大小不包括'\0',表示字符指针指向的空间的大小,即开辟的空间大小static size_t npos;
};
size_t string::npos = -1;

2.6赋值运算操作符重载

string& operator=(const string& s)
{if (this != &s){char* tmp = new char[s._size + 1];strcpy(tmp, s._str);//抛异常delete[]_str;_str = tmp;_size = s._size;_capacity = _size;}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)const
{return !strcmp(_str, s._str);
}
bool operator>=(const string& s)const
{return !(*this < s);
}
bool operator<=(const string& s)const
{return !(*this > s);
}

2.7. string类非成员函数

2.7.1介绍 

 2.7.2代码实现

	ostream& operator<<(ostream& out, string& d){for (auto& c : d)//不能out<<_str,因为可能中间有'\0'out << c;for (string::iterator it = d.begin(); it < d.end(); it++)cout << *it;return out;}istream& operator>>(istream& in, string& s){s.clear();char ch;char buff[128] = { 0 };int i = 0;ch = in.get();//直接in插入,接收不到空格while (ch != ' ' && ch != '\n'){if (i >= 0 && i < 127){buff[i++] = ch;}else if (i == 127){buff[127] = '\0';s += buff;s += ch;i = -1;}elses += ch;ch = in.get();}if (i >= 0)s += buff;return in;}

2.8string的插入和删除

	string& insert(size_t pos, const char* str)//在pos插入一个str指向的字符数组,原下标pos{                                         //以及之后的元素向后移位assert(pos <= _size);//插入位置是否越界int len = strlen(str);int i = _size + len;_str[i] = '\0';i--;if (_size + len > _capacity)reserve(_capacity + len);while (i >= pos + len)//类型转换{_str[i] = _str[i - len];i--;}i = strlen(str);while (i > 0){_str[pos + i - 1] = str[i - 1];i--;}_size += len;return *this;}string& insert(size_t pos,int size ,const char ch)//在pos插入目标字符,原下 //标pos以及之后的元素向后移位{assert(pos <= _size);//插入位置是否越界int i = _size + 1;if (_size + 1 > _capacity)reserve(_capacity *1.5);while (i >= pos + 1)//类型转换{_str[i] = _str[i - 1];i--;}_str[pos] =ch;i--;_size += 1;return *this;}string& erase(size_t pos, size_t len = npos)//npos是-1的无符号数强转,即42亿多,一个字符数组不会这末长,{                                         //结果就是把pos及后面的内容都删了。if (pos + len > _size || len == npos)//从下标pos出开始删除,一直删除狗len个元素或删完。{_size = pos;_str[_size] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}

2.9vsg++string结构的说明

注意:下述结构是在 32 位平台下进行验证, 32 位平台下指针占 4 个字节。

2.9.1vsstring的结构

string 总共占 28 个字节 ,内部结构稍微复杂一点,先是 有一个联合体,联合体用来定义 string 中字
符串的存储空间
当字符串长度小于 16 时,使用内部固定的字符数组来存放
当字符串长度大于等于 16 时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于 16 ,那 string 对象创建好之后,内
部已经有了 16 个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有 一个 size_t 字段保存字符串长度,一个 size_t 字段保存从堆上开辟空间总的容量
最后:还 有一个指针 做一些其他事情。
故总共占 16+4+4+4=28 个字节。

2.9.2g++string的结构

G++ 下, string 是通过写时拷贝实现的, string 对象总共占 4 个字节,内部只包含了一个指针,该指
针将来指向一块堆空间,内部包含了如下字段:
  1. 空间总大小
  2. 字符串有效长度
  3. 引用计数

3. string类的模拟实现

3.1统版写法的String

class String
{
public:String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}String& operator=(const String& s){if (this != &s){char* pStr = new char[strlen(s._str) + 1];strcpy(pStr, s._str);delete[] _str;_str = pStr;}return *this;}
~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};

3.2 现代版写法的String

class String
{
public:String(const char* str = ""){if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(nullptr){String strTmp(s._str);swap(_str, strTmp._str);}// 对比下和上面的赋值那个实现比较好?String& operator=(String s){swap(_str, s._str);return *this;}/*String& operator=(const String& s){if(this != &s){String strTmp(s);swap(_str, strTmp._str);}
return *this;}*/~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};

 总结:

事实上,string一共有100多个接口,但是有很多我们很少用到,今天所讲的也是较为常见的、使用频率较高的接口,如果以后什么时候有对别的接口的需求可以去文档里查看。

ok,那么,string,over!

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

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

相关文章

第十三届蓝桥杯物联网试题(省赛)

做后感悟&#xff1a; OLED显示函数需要一直显示&#xff0c;所以在主函数中要一直循环&#xff0c;为了确保这个检错功能error只输出一次&#xff0c;最好用中断串口进行接收数据&#xff0c;数据收完后自动进入中断函数中&#xff0c;做一次数据检查就好了&#xff0c;该开灯…

银行数字人民币系统应用架构设计

2019年10月&#xff0c;01区块链联合数字资产研究院发布了《人民币3.0&#xff1a;中国央行数字货币运行框架与技术解析》&#xff0c;从数字货币界定和人民币发展历程出发&#xff0c;区分了央行数字货币与比特币、移动支付等的区别&#xff0c;全面介绍了央行数字货币的发展历…

Linux设备驱动开发 - 三色LED呼吸灯分析

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 展锐UIS7885呼吸灯介绍呼吸灯调试方法亮蓝灯亮红灯亮绿灯展锐UIS7885呼吸灯DTS配置ump9620 PMIC驱动ump9620中的LED呼吸灯驱动LED的tr…

代码+视频,R语言logistic回归交互项(交互作用)的可视化分析

交互作用效应(p for Interaction)在SCI文章中可以算是一个必杀技&#xff0c;几乎在高分的SCI中必出现&#xff0c;因为把人群分为亚组后再进行统计可以增强文章结果的可靠性&#xff0c;不仅如此&#xff0c;交互作用还可以使用来进行数据挖掘。在既往文章中&#xff0c;我们已…

【理解机器学习算法】之分类问题的模型评估(ROC-AUC)

ROC曲线&#xff08;接收者操作特性曲线&#xff09;和AUC&#xff08;曲线下面积&#xff09;是在不同阈值设置下&#xff0c;用于分类问题的性能度量工具。下面是它们所代表的含义以及使用方法&#xff1a; ROC曲线 代表含义&#xff1a;ROC曲线是一个图形化的表示&#xf…

掌握收支明细,轻松记录收支明细,一键打印财务自由

在繁忙的生活中&#xff0c;你是否曾感到对个人的财务状况一头雾水&#xff1f;是否曾在需要证明收支情况时手忙脚乱&#xff0c;翻箱倒柜地寻找凭证&#xff1f;今天&#xff0c;我要向你揭示一个简单而高效的方法&#xff0c;帮助你轻松记录收支明细&#xff0c;并一键打印&a…

Cesium 默认选中框设置

Cesium 默认提供的选中框 进来关注下不迷人&#xff0c;还有更多集合专栏&#xff01; 1、设置状态&#xff1a; 设置false 就不会出现这个默认的状态&#xff0c;如果需要修改如下&#xff1a; viewer._selectedEntitynewEntity newEntity 就是你需要设置选中框的样式 可以…

BGP4+简介

定义 BGP是一种用于自治系统AS&#xff08;Autonomous System&#xff09;之间的动态路由协议&#xff0c;常用版本是BGP-4&#xff0c;BGP-4只能传递IPv4路由。针对IPv6的BGP4扩展&#xff0c;通常称为BGP4。 目的 BGP4用于在AS之间传递路由信息&#xff0c;并不是所有情况…

python - 更改pdf中文本的字体高亮颜色(fitz模块)

import fitzdoc fitz.open(r"e:/test.pdf") pagedoc[0]# 按照指定的位置设置颜色 highlight page.add_highlight_annot((20, 500,60, 520)) highlight.set_colors(stroke[1, 1, 0]) # light red color (r, g, b) 颜色rgb每个除以255得出 highlight.update()# 按照…

YZ系列工具之YZ09: VBA_Excel之读心术

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

大数据分析-基于Python的电影票房信息数据的爬取及分析

概要 现如今&#xff0c;人民群众对物质生活水平的要求已不再局限于衣食住行&#xff0c;对于精神文化有了更多的需求。电影在我国越来越受欢迎&#xff0c;电影业的发展越来越迅猛&#xff0c;为了充分利用互联网技术的发展&#xff0c;掌握电影业的态势&#xff0c;对信息进行…

uniapp 写安卓app,运行到手机端 调试

手机 设置》关于手机》点击版本号 4-5次&#xff0c;弹出手机锁屏页面&#xff0c;输入手机锁屏密码 2.手机 设置中 》搜索 开发人员选项 》 调试》打开USB调试 同页面 找到 选择USB配置》选择 MIDIhbuilder 编辑器 点击 》运行》运行到手机或模拟器》运行到Android App基座 》…

打卡--MySQL8.0 三 (SQL语言)

SQL语言介绍 SQL 是 Structured Query Language&#xff08;结构化查询语言&#xff09;的缩写。 SQL 是用来与关系数据库进行通信的。 它是关系数据库管理系统的标准语言。 SQL 语句用于执行任务&#xff0c;如更新数据库中的数据&#xff0c;或从数据库中检索数据。 一些常见…

【滑动窗口】长度最小的子数组|无重复字符的最长子串|最大连续1的个数 III|将 x 减到 0 的最小操作数

1. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 1.题目解析&#xff1a; 2.算法原理 &#xff08;1&#xff09;方法一&#xff1a;暴力列举出所有的子数组的和 时间复杂度&#xff1a;O&#xff08;n**2&#xff09;&#xff1a;枚举所有子数组O&#xff08;…

在Windows中安装wsl2和ubuntu22.04

目录 一、概述二、安装wsl22.1 虚拟化设置2.2 虚拟化设置2.3 切换和更新wsl2 三、安装ubuntu3.1 下载Ubuntu22.043.2 配置Ubuntu22.04 一、概述 wsl2是一种面向Windows操作系统的虚拟化技术&#xff0c;可以让我们在Windows操作系统中“丝滑”的运行Linux系统。wsl2由微软团队…

WMI接口设计实现

WMI是Windows操作系统管理数据和操作的基础设施&#xff0c;系统管理员可以使用VB Script、PowerShell及Windows API&#xff08;C、C#等&#xff09;管理本地或远程计算机。 使用WMI框架应用程序可以直接访问EC RAM、 I/O端口、Memory地址、寄存器、Setup NV设定值&#xff0c…

ideaSSM 财务凭证管理系统bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 财务凭证管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff…

【史上最全面arduino esp32教程】ESP32Time时间库

文章目录 前言一、安装ESP32Time库二、ESP32Time使用2.1 基础使用构造ESP32Time对象设置当前时间获取当前时间结构体 2.2 其他函数 总结 前言 欢迎来到这篇Arduino ESP32教程&#xff01;在本教程中&#xff0c;我们将介绍ESP32Time时间库的使用。时间在许多项目中起着重要的作…

基于SSM的手机商城管理系统+数据库+论文+免费远程调试

项目介绍: 基于SSM的手机商城管理系统。Javaee项目&#xff0c;采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc Mybatis JspBootstrapLayui来实现。MySQL数据库作为系统…

html第一次作业

常用标签 0, 骨架&#xff08;&#xff01;tap&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><t…