string类(详解)

 为什么学习string类?

1.1 C语言中的字符串

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

1.2面试题

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

string类oj题

344. 反转字符串 - 力扣(LeetCode)

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

LCR 192. 把字符串转换成整数 (atoi) - 力扣(LeetCode)

字符串最后一个单词的长度_牛客题霸_牛客网

125. 验证回文串 - 力扣(LeetCode)

541. 反转字符串 II - 力扣(LeetCode)

557. 反转字符串中的单词 III - 力扣(LeetCode)

43. 字符串相乘 - 力扣(LeetCode)

标准库中的string

推荐一个c++函数网站:cplusplus

2.1 string(了解)

https://cplusplus.com/reference/string/string/?kw=string(可复制链接查看string类)

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char,char_traits,allocator>  string;

4. 不能操作多字节或者变长字符的序列。

string类常用接口

1. string类对象的常见构造

6a232d6d53414dfda324b2e863ea37a2.jpeg

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

2. string类对象的容量操作

3878101e50a84df089b7317308f02faa.png

vs是1.5倍扩容,linux是2倍扩容

vs会多开一点空间,linux说开多少开多少

linux缩容不会删除数据,但是会缩小到size

vs不会缩容

注意:

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

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

5150bf6a67864475a210a18b8a689954.png

4. string类对象的修改操作

7e887684adc94762bb50a6e760a2c90a.png

注意:

1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5. string类非成员函数

65e1d1e9f65b439d9590ec37f86e726c.png

上面的几个接口大家了解一下,OJ题目中会有一些体现他们的使用。string类中还有一些其他的操作,这里不一一列举,大家在需要用到时不明白了查文档即可。

string类的模拟实现完整代码

已交代完毕,代码无误

string.h

# define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h> 
#include<iostream>using namespace std;namespace jzy
{class string{public:typedef 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;}const char* c_str() const{return _str;}size_t size() const{return _size;}string(const char* str = "");// ִдstring(const string& s);string& operator=(string s);~string();const char& operator[](size_t pos) const;char& operator[](size_t pos);void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);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);size_t find(char ch, size_t pos = 0);//21:10size_t find(const char* str, size_t pos = 0);string substr(size_t pos = 0, size_t len = npos);void clear();private:size_t _capacity = 0;size_t _size = 0;char* _str = nullptr;const static size_t npos = -1;};istream& operator>>(istream& in, string& s);ostream& operator<<(ostream& out, const string& s);
}

string.cpp

#define CRT_SECURE_NO_WARNINGS 1
#include"string.h"namespace jzy
{string::string(const char* str)//默认构造给一个空串{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}//拷贝构造现代写法string::string(const string& s){string tmp(s._str);swap(tmp);}//赋值重载现代写法string& string::operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}const char& string::operator[](size_t pos) const{assert(pos <= _size);return _str[pos];}char& string::operator[](size_t pos){assert(pos <= _size);return _str[pos];}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];--end;}strncpy(_str + pos, str, len);_size += len;}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}string string::substr(size_t pos, size_t len){assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}void string::clear(){_size = 0;_str[0] = '\0';}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char buff[128];char ch = in.get();int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include"string.h"namespace jzy
{void print_str(const string& s){for (size_t i = 0; i < s.size(); i++){cout << s[i] << " ";}cout << endl;string::const_iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;}void test_string1(){string s1("hello world");cout << s1.c_str() << endl;string s2;cout << s2.c_str() << endl;for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1.c_str() << endl;string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;for (auto ch : s1){cout << ch << " ";}cout << endl;print_str(s1);}void test_string2(){string s1("hello world");cout << s1.c_str() << endl;s1 += ' ';s1 += "xxxxxx";cout << s1.c_str() << endl;s1.insert(5, 'y');s1.insert(5, 'y');s1.insert(5, 'y');cout << s1.c_str() << endl;s1.insert(0, 'y');cout << s1.c_str() << endl;s1.insert(0, "zzzzzzz");cout << s1.c_str() << endl;}void test_string3(){string s1("hello world");cout << s1.c_str() << endl;s1.erase(5, 4);cout << s1.c_str() << endl;s1.erase(5, 100);cout << s1.c_str() << endl;s1.erase(2);cout << s1.c_str() << endl;}void test_string4(){string s1("hello world");string s2("xxxx");std::swap(s1, s2);s1.swap(s2);string str("https://legacy.cplusplus.com/reference/string/string/substr/");string sub1, sub2, sub3;size_t pos1 = str.find(':');sub1 = str.substr(0, pos1 - 0);cout << sub1.c_str() << endl;size_t pos2 = str.find('/', pos1 + 3);sub2 = str.substr(pos1 + 3, pos2 - (pos1 + 3));cout << sub2.c_str() << endl;sub3 = str.substr(pos2 + 1);cout << sub3.c_str() << endl;}void test_string5(){string s1("hello world");string s2(s1);string s3("xxxx");s1 = s3;}void test_string6(){string s1("hello world");string s2(s1);string s3("xxxx");s1 = s3;cout << s1.c_str() << endl;cout << s1 << endl;cin >> s1;cout << s1 << endl;/*char ch1, ch2;cin >> ch1 >> ch2;*/cin >> s2;cout << s2 << endl;}void test_string7(){string s1("hello world");cout << s1.c_str() << endl;cout << s1 << endl;s1.clear();cout << s1.c_str() << endl;cout << s1 << endl;}void test_string8(){string s1("hello world");string s2(s1);cout << s1 << endl;cout << s2 << endl;string s3("xxxxxxxxxxxxxxxx");s1 = s3;cout << s1 << endl;cout << s3 << endl;}
}int main()
{jzy::test_string2();return 0;
}

模拟实现讲解

 默认在.h有缺省值“”,意思是默认构造一个空字符串,string构造,先求出长度赋值给_size,然后容量是有效字符个数,最后_str new出来容量+1的空间,意思是给\0腾出一个空间,这流程下来只用strlen一次,减少时间复杂度的消耗,开好空间最后再拷贝过去

交换有3个,库里的2个,string库里1个,因为默认的swap内置类型没事,自定义类型会深拷贝,所以默认用string类型交换都会转换成调用string库里边的,string库里边默认是调用默认的swap,内置类型直接值交换就行,减少深拷贝的损耗

s1拷贝构造s2,先构造一个tmp,用tmp和s2交换,tmp出作用域调用析构函数,s2会指向拷贝构造完成的新空间

赋值拷贝也很简单,先让s3拷贝构造一下s,交换s和s1,s1指向赋值后新空间,s出作用域调用析构,直接释放空间

析构函数,先delete释放空间,置空,内置类型置0

[],非const类型调用非const[],返回可以修改的char&,const类型调用const[],返回不可修改的const char&

reserve是修改容量, 这里我们只扩容不缩容,申请新空间(多申请一个是给\0,容量是存放有效字符个数),将值拷贝过去,析构_str,_str指向新空间,容量置n

尾插还是老一套,判断是否需要扩容,尾插,size++,将下一个位置置\0(因为每次都会多开一个空间,所以永远不会越界)

这个append是老一套,尾插一个字符串,先判断是否要扩容,在拷贝过去

+=字符很简单,直接复用尾插,返回*this,+=后的结果

+=字符串,复用append逻辑,返回+=后的结果

插入逻辑,和顺序表的随机插入有异曲同工之妙,先向后逐一挪动字符,再在对应位置插入某个字符

任意位置插入字符串,逻辑相似,不再赘述

erase,删除某一个位置长度是len的字符串/字符,如果长度是npos(无符号整数最大值)或者pos+len大于等于有效字符个数,就直接把pos位置和之后的字符全部删除;如果删除的长度小于有效字符个数,将剩余的字符拷贝到pos位置,将_size置为删除后的值

从某个位置找某个字符,找到返回这个值的下标,找不到返回npos

利用strstr找到那个字符串,返回下标,找不到返回nops

清理字符串,直接_size是0,第一个位置填充\0就行

流插入(输出),直接范围for(迭代器)打印字符类型,返回字符串

流提取(输入),按照库里边是遇到空格或者换行结束

getline是提取字符串一行,遇到\n结束(遇到空格不结束)

cin是提取字符串遇到空格或者换行结束

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

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

相关文章

【Redis】set 和 zset 类型的介绍和常用命令

1. set 1.1 介绍 set 类型和 list 不同的是&#xff0c;存储的元素是无序的&#xff0c;并且元素不允许重复&#xff0c;Redis 除了支持集合内的增删查改操作&#xff0c;还支持多个集合取交集&#xff0c;并集&#xff0c;差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …

一些计算机零碎知识随写(25年2月)

今天复习 MySQL 的时候&#xff0c;我突然冒出一个想法&#xff1a;能不能远程连接 MySQL 呢&#xff1f;虽说心里清楚理论上可行&#xff0c;但一直没实际操作过。 于是&#xff0c;起床后我立马打开服务器&#xff0c;准备启动 MySQL。结果&#xff0c;这一启动就发现问题了&…

ESP32-c3实现获取土壤湿度(ADC模拟量)

1硬件实物图 2引脚定义 3使用说明 4实例代码 // 定义土壤湿度传感器连接的模拟输入引脚 const int soilMoisturePin 2; // 假设连接到GPIO2void setup() {// 初始化串口通信Serial.begin(115200); }void loop() {// 读取土壤湿度传感器的模拟值int sensorValue analogRead…

Java 大视界 -- Java 大数据在量子通信安全中的应用探索(69)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

86.(2)攻防世界 WEB PHP2

之前做过&#xff0c;回顾一遍&#xff0c;详解见下面这篇博客 29.攻防世界PHP2-CSDN博客 既然是代码审计题目&#xff0c;打开后又不显示代码&#xff0c;肯定在文件里 <?php // 首先检查通过 GET 请求传递的名为 "id" 的参数值是否严格等于字符串 "admi…

从理论到实践:Linux 进程替换与 exec 系列函数

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 在Linux中&#xff0c;进程替换&#xff08;Process Substitution&#xff09;是一个非常强大的特性&#xff0c;它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中…

【数据结构】初识链表

顺序表的优缺点 缺点&#xff1a; 中间/头部的插入删除&#xff0c;时间复杂度效率较低&#xff0c;为O(N) 空间不够的时候需要扩容。 如果是异地扩容&#xff0c;增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间&#xff0c;会有不小的消耗。 扩容可能会存在…

增删改查(CRUD)操作

文章目录 MySQL系列&#xff1a;1.CRUD简介2.Create(创建)2.1单行数据全列插入2.2 单行数据指定插入2.3 多⾏数据指定列插⼊ 3.Retrieve(读取)3.1 Select查询3.1.1 全列查询3.1.2 指定列查询3.1.3 查询字段为表达式&#xff08;都是临时表不会对原有表数据产生影响&#xff09;…

使用Pygame制作“贪吃蛇”游戏

贪吃蛇 是一款经典的休闲小游戏&#xff1a;玩家通过操控一条会不断变长的“蛇”在屏幕中移动&#xff0c;去吃随机出现的食物&#xff0c;同时要避免撞到墙壁或自己身体的其他部分。由于其逻辑相对简单&#xff0c;但可玩性和扩展性都不错&#xff0c;非常适合作为新手练习游戏…

JavaScript闭包深入剖析:性能剖析与优化技巧

一、引言 在 JavaScript 的奇妙世界里&#xff0c;闭包无疑是一个既强大又迷人的特性。它就像是一把万能钥匙&#xff0c;为开发者打开了实现各种高级功能的大门。从数据封装与保护&#xff0c;到函数的记忆化&#xff0c;再到模块化开发&#xff0c;闭包都发挥着举足轻重的作…

蓝桥杯嵌入式赛道备考1 —— 基础GPIO实战

1. 点亮一个LED 蓝桥杯的板子资料的URL&#xff0c;笔者是从GitHub - JoyRiderJie/LanQiaoBei-QianRuShi拉去下来的。这个是Github仓库地址。 从应用层去玩一个开发板子&#xff0c;首先需要的是去尝试是点亮一个LED。让我们切换到手册《CT117E——产品手册》的第11页&#x…

浅析DNS污染及防范

DNS污染&#xff08;DNS Cache Poisoning&#xff09;是一种网络攻击手段&#xff0c;通过篡改DNS服务器的缓存数据&#xff0c;将域名解析结果指向错误的IP地址&#xff0c;从而误导用户访问恶意网站或无法访问目标网站。这种攻击利用了DNS协议的特性&#xff0c;例如“只认第…

AI编程:如何编写提示词

这是小卷对AI编程工具学习的第2篇文章&#xff0c;今天讲讲如何编写AI编程的提示词&#xff0c;并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是&#xff1a;目标清晰明确&#xff0c;具有针对性&#xff0c;能引导模型理解问题 下面是两条提示词的对…

linux asio网络编程理论及实现

最近在B站看了恋恋风辰大佬的asio网络编程&#xff0c;质量非常高。在本章中将对ASIO异步网络编程的整体及一些实现细节进行完整的梳理&#xff0c;用于复习与分享。大佬的博客&#xff1a;恋恋风辰官方博客 Preactor/Reactor模式 在网络编程中&#xff0c;通常根据事件处理的触…

【思维导图】并发编程

学习计划&#xff1a;将目前已经学的知识点串成一个思维导图。在往后的学习过程中&#xff0c;不断往思维导图里补充&#xff0c;形成自己整个知识体系。对于思维导图里的每个技术知识&#xff0c;自己用简洁的话概括出来&#xff0c; 训练自己的表达能力。 并发和并行的区别 并…

【B站保姆级视频教程:Jetson配置YOLOv11环境(四)cuda cudnn tensorrt配置】

Jetson配置YOLOv11环境&#xff08;4&#xff09;cuda cudnn tensorrt配置 文章目录 0. 简介1. cuda配置&#xff1a;添加cuda环境变量2. cudnn配置3. TensorRT Python环境配置3.1 系统自带Python环境中的TensorRT配置3.2 Conda 虚拟Python环境中的TensorRT配置 0. 简介 官方镜…

【深度分析】DeepSeek 遭暴力破解,攻击 IP 均来自美国,造成影响有多大?有哪些好的防御措施?

技术铁幕下的暗战&#xff1a;当算力博弈演变为代码战争 一场针对中国AI独角兽的全球首例国家级密码爆破&#xff0c;揭开了数字时代技术博弈的残酷真相。DeepSeek服务器日志中持续跳动的美国IP地址&#xff0c;不仅是网络攻击的地理坐标&#xff0c;更是技术霸权对新兴挑战者的…

如何在数据湖中有效治理和管理“数据沼泽”问题,提高数据的可发现性和利用率?

在数据湖中有效治理和管理“数据沼泽”问题&#xff0c;提高数据的可发现性和利用率&#xff0c;需要从多个方面入手&#xff0c;包括数据治理、元数据管理、数据质量控制、安全性保障以及生命周期管理等。以下是具体的策略和方法&#xff1a; 1. 构建强大的数据治理框架 数据…

【4Day创客实践入门教程】Day3 实战演练——桌面迷你番茄钟

Day3 实战演练——桌面迷你番茄钟 目录 Day3 实战演练——桌面迷你番茄钟1. 选择、准备元件、收集资料2. 硬件搭建3.编写代码 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟…

Oracle Primavera P6自动进行进度计算

前言 在P6 Professional 有一个自动计划计算的选项&#xff0c;很多人不了解该设置如何使用&#xff0c;以及什么时候该启动这项配置。 详情 P6 Professional 默认为非自动进度计算。启用自动选项后&#xff0c;可以快速查看调度更改的效果。 ​ ​ 如图所示&#xff0c;当你…