当前位置: 首页 > article >正文

C++ STL—— String库

        在C++编程中,字符串操作是几乎每个项目都会涉及的基础功能。C++标准模板库(STL)中的string为我们提供了强大而灵活的工具,使得字符串的处理变得简单高效。无论是字符串的创建、修改、查找,还是复杂的文本处理string库都能轻松应对。

一、常用的String函数:

函数功能
length( )返回字符串的长度
size( )返回字符串的长度
push_back( )在字符串尾部添加一个字符
append( )在字符串尾部添加一个字符串
find(str,pos)查找str在pos(含)之后第一次出现的位置,若不写pos,默认为pos=0。如果找不到,返回-1,注意需要强制转换为int型才能输出-1(后面的模拟实现会讲到)
substr(pos,len)返回从pos位置开始截取最多len个字符组成的字符串,如果从pos开始的子串长度不足len,则截取这个子串
insert(index,count,str)在index出连续插入count次字符串str
insert(index,str)在index处插入字符串str
erase(index,count)将字符串从index位置开始(含)的count个字符删除,若不传参给count,则表示删除index位置及以后的所有字符
replace(pos,count,str)把从pos位置开始count个字符的子串替换为str
replace(first,last,str)把以first开始(含)、last结束(不含)的子串(左闭右开区间)替换为str,其中first和last均为迭代器
empty( )判断字符串是否为空,如果为空返回1,不为空则返回0

二、底层实现

        以下是 C++ STL 中 std::string 类相关运算符和成员函数的底层实现原理的详细说明。由于标准库的具体实现因编译器而异(如 GCC 的 libstdc++ 或 LLVM 的 libc++),此处描述的是通用的设计思路和典型实现方式。


1、运算符重载的底层实现

1. operator+(字符串连接)

  • 功能:将两个字符串连接成一个新字符串。

  • 底层实现

    std::string operator+(const std::string& lhs, const std::string& rhs) {std::string result;result.reserve(lhs.size() + rhs.size()); // 预分配内存result = lhs;                            // 复制左操作数result.append(rhs);                      // 追加右操作数return result;
    }
    • 创建新字符串对象,分配足够内存(lhs.size() + rhs.size())。

    • 依次复制左、右操作数的字符内容。

    • 返回新对象(可能触发移动语义优化,避免深拷贝)。

2. operator==(相等比较)

  • 功能:判断两个字符串内容是否相同。

  • 底层实现

    bool operator==(const std::string& lhs, const std::string& rhs) {if (lhs.size() != rhs.size()) return false;       // 长度不同直接返回 falsereturn memcmp(lhs.data(), rhs.data(), lhs.size()) == 0; // 逐字节比较
    }
    • 先比较长度,长度不同直接返回 false

    • 使用 memcmp 快速比较内存块。

3. operator< 和 operator>(字典序比较)

  • 功能:按字典序比较字符串。

  • 底层实现(以 < 为例):

    bool operator<(const std::string& lhs, const std::string& rhs) {size_t min_len = std::min(lhs.size(), rhs.size());int cmp = memcmp(lhs.data(), rhs.data(), min_len); // 比较前 min_len 个字符if (cmp != 0) return cmp < 0;                     // 发现不同字符return lhs.size() < rhs.size();                   // 前面字符全相同,比较长度
    }
    • 逐字符比较,直到找到第一个不同的字符。

    • 若所有字符相同,则长度较短的字符串更小。


2、成员函数的底层实现

1. length() 和 size()

  • 功能:返回字符串长度。

  • 底层实现

    size_t size() const noexcept {return _size; // 直接返回内部维护的 size 成员变量
    }
    size_t length() const noexcept {return _size; // 通常与 size() 完全等价
    }
    • 直接返回内部存储的 size 值,时间复杂度 O(1)

2. push_back(char c)

  • 功能:在字符串末尾追加一个字符。

  • 底层实现

    void push_back(char c) {if (_size + 1 > _capacity) {      // 容量不足时扩容reserve(_capacity * 2 + 1);   // 通常按指数扩容(如 2 倍)}_data[_size] = c;                 // 写入新字符_size++;                          // 更新长度_data[_size] = '\0';              // 添加终止符(某些实现可能省略)
    }
    • 若容量不足,触发扩容(reserve)。

    • 在末尾写入字符并更新长度,时间复杂度 平摊 O(1)

3. append(const std::string& str)

  • 功能:追加另一个字符串。

  • 底层实现

    std::string& append(const std::string& str) {if (_size + str.size() > _capacity) {reserve(_size + str.size());  // 确保容量足够}memcpy(_data + _size, str.data(), str.size()); // 复制内容_size += str.size();              // 更新长度return *this;
    }
    • 类似 operator+,但直接在原字符串上操作,时间复杂度 O(n)

4. find(const std::string& str, size_t pos)

  • 功能:从 pos 位置开始查找子串 str

  • 底层实现(简化版):

    size_t find(const std::string& str, size_t pos = 0) const {if (pos > _size || str.size() > _size - pos) return npos;for (size_t i = pos; i <= _size - str.size(); ++i) {if (memcmp(_data + i, str.data(), str.size()) == 0) {return i; // 找到匹配}}return npos;       // 未找到
    }
    • 暴力匹配算法,时间复杂度 O(n*m)(实际可能优化为 KMP 或 Boyer-Moore)。

        在C++中,string::find函数的返回值类型为size_t(无符号整数类型)。当未找到目标时,它返回string::npos,其值实际上是size_t类型的最大值(例如,64位系统中为18446744073709551615)。以下是关键原因:

为什么强制转换为int会输出-1?

  1. 无符号到有符号的转换
    size_t是无符号的,而int是有符号的。当string::npos(即size_t最大值)被强制转换为int时,会发生二进制位的直接截断
    例如,size_t18446744073709551615(二进制全1)转换为int时,会解释为补码形式的-1。

  2. 实现依赖行为
    这种转换的结果依赖于平台和编译器。若int为32位,则高位被丢弃,剩余的32位全1即对应-1;若int为64位,则结果可能不同。因此,强制转换并非完全可靠

示例验证

#include <iostream>
#include <string>
using namespace std;int main() {string s = "abc";size_t pos = s.find("d"); // 未找到,pos = string::nposcout << "直接输出 size_t: " << pos << endl;      // 输出 18446744073709551615cout << "强制转为 int: " << (int)pos << endl;   // 输出 -1return 0;
}

正确做法

  1. 直接比较是否等于string::npos

    if (s.find("xxx") == string::npos) {cout << "未找到" << endl;
    }
  2. 避免依赖强制转换
    强制转换可能导致不可移植性,尤其在跨平台代码中。若必须输出-1,可以显式判断:

    size_t pos = s.find("xxx");
    cout << (pos == string::npos ? -1 : (int)pos) << endl;

总结

  • find未找到时返回string::npos(无符号最大值),直接输出会显示大整数。

  • 强制转换为int后输出-1,是因为二进制截断触发了补码的符号位机制,但这是实现相关的。

  • 推荐使用pos == string::npos进行逻辑判断,而非依赖强制转换后的值。

5. substr(size_t pos, size_t len)

  • 功能:返回从 pos 开始的长度为 len 的子串。

  • 底层实现

    std::string substr(size_t pos = 0, size_t len = npos) const {len = std::min(len, _size - pos); // 修正 len 为有效值return std::string(_data + pos, len); // 直接构造新 string
    }
    • 构造新字符串并复制指定区间的字符,时间复杂度 O(len)

6. insert(size_t index, size_t count, char c) 和 insert(size_t index, const std::string& str)

  • 功能:在指定位置插入字符或字符串。

  • 底层实现(插入字符串):

    std::string& insert(size_t index, const std::string& str) {if (index > _size) throw std::out_of_range(...);if (_size + str.size() > _capacity) reserve(_size + str.size());memmove(_data + index + str.size(), _data + index, _size - index); // 后移原有字符memcpy(_data + index, str.data(), str.size());     // 插入新内容_size += str.size();                              // 更新长度return *this;
    }
    • 使用 memmove 处理内存重叠,时间复杂度 O(n)

7. erase(size_t index, size_t count)

  • 功能:删除从 index 开始的 count 个字符。

  • 底层实现

    std::string& erase(size_t index, size_t count = npos) {count = std::min(count, _size - index);          // 修正实际删除长度memmove(_data + index, _data + index + count, _size - index - count);_size -= count;                                  // 更新长度_data[_size] = '\0';                             // 终止符return *this;
    }
    • 用 memmove 将后续字符前移覆盖被删除部分,时间复杂度 O(n)

8. replace(size_t pos, size_t count, const std::string& str)

  • 功能:替换从 pos 开始的 count 个字符为 str

  • 底层实现

    std::string& replace(size_t pos, size_t count, const std::string& str) {erase(pos, count);                   // 删除旧内容insert(pos, str);                   // 插入新内容return *this;
    }
    • 组合 erase 和 insert,时间复杂度 O(n + m)

9. empty()

  • 功能:判断字符串是否为空。

  • 底层实现

    bool empty() const noexcept {return _size == 0; // 直接检查长度是否为 0
    }
    • 时间复杂度 O(1)


3、关键实现细节

  1. 内存管理

    • 使用动态数组(char* _data)存储字符。

    • 维护 size(当前长度)和 capacity(当前容量)。

    • 扩容时通常按指数增长(如 reserve(2 * _capacity))以平摊时间复杂度。

  2. 短字符串优化(SSO)

    • 短字符串(如长度 ≤15)直接存储在对象内部,避免堆内存分配。

  3. 异常安全

    • 内存分配失败时可能抛出 std::bad_alloc

  4. 移动语义(C++11)

    • 移动构造函数直接“窃取”源对象的 _data,避免深拷贝。

三、 String库函数的直接应用

# include <bits/stdc++.h>
using namespace std;
int main()
{//定义、初始化、赋值string s1;  // 定义一个空字符串s1string s2 = "bcd";  // 定义并初始化字符串s2为"bcd"s2 = "efg";  // 将s2重新赋值为"efg"cout << s2 << endl;  // 输出s2的值。输出:efgstring s("abc");  // 定义并初始化字符串s为"abc"string s3(s);  // 定义字符串s3并用s初始化,s3为"abc"//长度cout << s.length() << endl;  // 输出字符串s的长度。输出:3//遍历for (int i = 0; i < s.size(); i++) cout << s[i]; cout << endl;  // 遍历并输出字符串s的每个字符。输出:abc//添加,合并字符串s.push_back('d'); cout << s << endl;  // 在s的末尾添加字符'd'。输出:abcds.append('efg'); cout << s << endl;  // 在s的末尾添加字符串"efg"。输出:abcdefgs = s + 'h'; s += 'i'; cout << s << endl;  // 在s的末尾添加字符'h'和'i'。输出:abcdefghi//用重载的 + 在尾部添加字符。输出:abcdefgh1s = s + "jk"; s += "lmnabc"; s = "xyz" + s; cout << s << endl;  // 在s的末尾添加字符串"jk"和"lmnabc",并在s的开头添加"xyz"。输出:xyzabcdefghijklmnabc//输出:xyzabcdefghijklmnabcstring s4 = "uvw";  // 定义并初始化字符串s4为"uvw"cout << s + s4 << endl; //合并字符串。输出:xyzabcdefghijklmnabcuvw//查找字符串字符串cout << "pos of b = "<<s.find('b') << endl;  // 查找字符'b'第一次出现的位置。输出:pos of b = 4cout << "pos of ef = "<<s.find('ef') << endl;  // 查找字符串"ef"第一次出现的位置。输出:pos of ef = 8cout << "pos of ab = "<<s.find('ab',5) << endl;  // 从s[5]开始查找字符串"ab"第一次出现的位置。输出:pos of ab = 18cout << "pos of hello = "<<(int)s.find('hello') << endl;  // 查找字符串"hello"第一次出现的位置,未找到返回-1。输出:pos of hello = -1//截取字符串cout << s.substr(3, 5) << endl;  // 从s[3]开始截取5个字符构成的字符串。输出:abcdecout << s.insert(4,"opq") << endl;  // 在s[4]位置插入字符串"opq"。输出:xyzoppqabcdefghijklmnabc//删除、替换cout << s.erase(10,2)<< endl;//从s[10]开始删除两个字符。输出:xyzaopqbcdghijklmnabccout << s.erase(10)<< endl;//从s[10]开始删除后面的所有字符。输出:xyzaopqbcdcout << s.replace(2,3,"1234")<< endl;//把从s[2]开始的3个字符替换为"1234"。输出:xy1234pqbcdcout << s.replace(s.begin() + 7,s.begin() + 9,"5678")<< endl;//把s[7]->s[8]替换为"1234"。输出:xy1234p5678cd//清理、判断cout << s.empty()<< endl;    //判断是否为空,不空返回0,空返回1。输出:0s.clear();    //清空cout << s.empty()<< endl;    //输出:1//比较string s5 = "abc"; string s6 = "abc"; string s7 = "bc";if(s5 == s6) cout << "== "<< endl;if(s5 < s7) cout << "<" << endl;if(s5 > s7) cout << ">" << endl;if(s5 != s7) cout << "!=" << endl;return 0;
}

相关文章:

C++ STL—— String库

在C编程中&#xff0c;字符串操作是几乎每个项目都会涉及的基础功能。C标准模板库&#xff08;STL&#xff09;中的string类为我们提供了强大而灵活的工具&#xff0c;使得字符串的处理变得简单高效。无论是字符串的创建、修改、查找&#xff0c;还是复杂的文本处理&#xff0c…...

【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数

【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数 关系数据库结构化查询语言SQL数据定义语言(DDL)数据查询语言(Data Query Language, DQL)数据操纵语言(Data Manipulation Language, DML)数据控制语言(Data Control Language, DCL)关系型数据库的优…...

DoS攻击防范

一、网络架构优化 使用CDN或反向代理 通过内容分发网络&#xff08;CDN&#xff09;或反向代理&#xff08;如Nginx&#xff09;分散流量&#xff0c;将请求分发到多个服务器节点&#xff0c;减轻单点压力&#xff0c;同时过滤异常请求。 负载均衡技术 部署负载均衡设备&#…...

Linux驱动开发实战(四):设备树点RGB灯

Linux驱动开发实战&#xff08;四&#xff09;&#xff1a;设备树点RGB灯 文章目录 Linux驱动开发实战&#xff08;四&#xff09;&#xff1a;设备树点RGB灯前言一、驱动实现1.1 驱动设计思路1.2 关键数据结构1.3 字符设备操作函数1.4 平台驱动探测函数1.5 匹配表和平台驱动结…...

vue中,watch里,this为undefined的两种解决办法

提示&#xff1a;vue中&#xff0c;watch里&#xff0c;this为undefined的两种解决办法 文章目录 [TOC](文章目录) 前言一、问题二、方法1——使用function函数代替箭头函数()>{}三、方法2——使用that总结 前言 ‌‌‌‌‌尽量使用方法1——使用function函数代替箭头函数()…...

设计模式C++

针对一些经典的常见的场景, 给定了一些对应的解决方案&#xff0c;这个就叫设计模式。 设计模式的作用&#xff1a;使代码的可重用性高&#xff0c;可读性强&#xff0c;灵活性好&#xff0c;可维护性强。 设计原则&#xff1a; 单一职责原则&#xff1a;一个类只做一方面的…...

前端构建工具进化论:从Grunt到Turbopack的十年征程

前端构建工具进化论&#xff1a;从Grunt到Turbopack的十年征程 一、石器时代&#xff1a;任务自动化工具&#xff08;2012-2014&#xff09; 1.1 Grunt&#xff1a;首个主流构建工具 // Gruntfile.js 典型配置 module.exports function(grunt) {grunt.initConfig({concat: {…...

设备预测性维护:企业降本增效的关键密码​

在当今竞争激烈的商业战场中&#xff0c;企业犹如一艘在波涛汹涌大海上航行的巨轮&#xff0c;要想乘风破浪、稳步前行&#xff0c;降本增效便是那至关重要的 “船锚”&#xff0c;帮助企业在复杂的市场环境中站稳脚跟。而设备预测性维护&#xff0c;正是开启企业降本增效大门的…...

css基本功

为什么 ::first-letter 是伪元素&#xff1f; ::first-letter 的作用是选择并样式化元素的第一个字母&#xff0c;它创建了一个虚拟的元素来包裹这个字母&#xff0c;因此属于伪元素。 grid布局 案例一 <!DOCTYPE html> <html lang"zh-CN"><head&…...

信号处理抽取多项滤波的数学推导与仿真

昨天的《信号处理之插值、抽取与多项滤波》&#xff0c;已经介绍了插值抽取的多项滤率&#xff0c;今天详细介绍多项滤波的数学推导&#xff0c;并附上实战仿真代码。 一、数学变换推导 1. 多相分解的核心思想 将FIR滤波器的系数 h ( n ) h(n) h(n)按相位分组&#xff0c;每…...

C++双端队列知识点+习题

在C中&#xff0c;双端队列&#xff08;Deque&#xff0c;发音为“deck”&#xff09;是标准模板库&#xff08;STL&#xff09;中的一种容器适配器&#xff0c;其全称为Double-Ended Queue。它结合了队列和栈的特点&#xff0c;允许在容器的两端&#xff08;前端和后端&#x…...

【递归、搜索和回溯算法】专题二 :二叉树中的深搜

二叉树中的深搜 深度优先遍历&#xff08;DFS&#xff09;&#xff1a;一种沿着树或图的深度遍历节点的算法&#xff0c;尽可能深地搜索树或图的分支&#xff0c;如果一条路径上的所有结点都被遍历完毕&#xff0c;就会回溯到上一层&#xff0c;继续找一条路遍历。 在二叉树中…...

Vue3计算属性深度解析:经典场景与Vue2对比

一、计算属性的核心价值 计算属性&#xff08;Computed Properties&#xff09;是Vue响应式系统的核心特性之一&#xff0c;它通过依赖追踪和缓存机制优雅地解决模板中复杂逻辑的问题。当我们需要基于现有响应式数据进行派生计算时&#xff0c;计算属性总能保持高效的性能表现…...

UE5与U3D引擎对比分析

Unreal Engine 5&#xff08;UE5&#xff09;和Unity 3D&#xff08;U3D&#xff09;是两款主流的游戏引擎&#xff0c;适用于不同类型的项目开发。以下是它们的主要区别&#xff0c;分点整理&#xff1a; 1. 核心定位 UE5&#xff1a; 主打3A级高画质项目&#xff08;如主机/P…...

【vue3学习笔记】(第150-151节)computed计算属性;watch监视ref定义的数据

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 本篇内容对应课程第150-151节 课程 P150节 《computed计算属性》笔记 写一个简单的 姓、名输入框效果&#xff1a; 用vue2的形式定义一个计算属性 fullName&#xff1a; 测试页面展示无问题&#xff1a; 但是&#xff0c;在vue…...

JavaScript如何实现复制图片功能?

最近开发中遇到一个需求&#xff0c;就是用户希望能通过直接点击按钮复制图片&#xff0c;然后就可以很方便的把图片发送到班群中&#xff0c;于是就有了复制图片的需求。 那么如何通过JavaScript来实现复制图片呢&#xff1f; 一、前置知识&#xff1a;如何实现复制&#xf…...

MySQL 8 设置允许远程连接(Windows环境)

&#x1f31f; MySQL 8 设置允许远程连接&#xff08;Windows环境&#xff09; 在开发和部署应用时&#xff0c;经常需要从远程主机连接到MySQL数据库。默认情况下&#xff0c;MySQL仅允许本地连接&#xff0c;因此需要进行一些配置才能允许远程访问。今天&#xff0c;我将详细…...

我又又又又又又更新了~~纯手工编写C++画图,有注释~~~

再再再次感谢Ttcofee提的问题 本次更新内容&#xff1a; 鼠标图案&#xff08;切换&#xff09;&#xff0c;版本号获取&#xff0c;输入框复制剪切板 提前申明&#xff1a;如果运行不了&#xff0c;请到主页查看RedpandaDevc下载&#xff0c;若还是不行就卸了重装。 版本号&…...

Python控制语句——循环语句-for

1.下面的语句哪个会无限循环下去()。 A、 for a in range(10): time.sleep(10) B、 while 1<10: time.sleep(10) C、 while True: break D、 a = [3,-1,2] for i in a: if i==-1: break 答案:B。1<10始终为True,循环体中又没有break的条件,故B会无限循环。 2.for s i…...

全面解析:将采购入库单数据集成到MySQL的技术实施

旺店通旗舰版-采购入库单集成到MySQL的技术案例分享 在数据驱动的业务环境中&#xff0c;如何高效、准确地实现系统间的数据对接是企业面临的重要挑战。本文将聚焦于一个具体的系统对接集成案例&#xff1a;将旺店通旗舰奇门平台上的采购入库单数据集成到MySQL数据库中&#x…...

12. Pandas :使用pandas读Excel文件的常用方法

一 read_excel 函数 其他参数根据实际需要进行查找。 1.接受一个工作表 在 11 案例用到的 Excel 工作簿中&#xff0c;数据是从第一张工作表的 A1 单元格开始的。但在实际场景中&#xff0c; Excel 文件可能并没有这么规整。所以 panda 提供了一些参数来优化读取过程。 比如 s…...

记录致远OA服务器硬盘升级过程

前言 日常使用中OA系统突然卡死&#xff0c;刷新访问进不去系统&#xff0c;ping服务器地址正常&#xff0c;立马登录服务器检查&#xff0c;一看磁盘爆了。 我大脑直接萎缩了&#xff0c;谁家OA系统配400G的空间啊&#xff0c;过我手的服务器没有50也是30台&#xff0c;还是…...

Java网络多线程

网络相关概念: 关于访问: IP端口 因为一个主机上可能有多个服务, 一个服务监听一个端口,当你访问的时候主机通过端口号就能知道要和哪个端口发生通讯.因此一个主机上不能有两个及以上的服务监听同一个端口. 协议简单来说就是数据的组织形式 好像是两个人交流一样,要保证自己说…...

【H2O2 | 软件开发】Axios发送Http请求

目录 前言 开篇语 准备工作 正文 概念 封装工具包 示例 结束语 前言 开篇语 本系列为短篇&#xff0c;每次讲述少量知识点&#xff0c;无需一次性灌输太多的新知识点。该主题文章主要是围绕前端、全栈开发相关面试常见问题撰写的&#xff0c;希望对诸位有所帮助。 如…...

VScode 运行LVGL

下载vscode解压 环境安装 安装mingw64&#xff0c;gcc 版本必须8.3以上 安装cmak 系统环境变量Path中添加&#xff08;以实际安装目录为准&#xff09; C:\Program Files\mingw64\bin C:\Program Files\CMake\bin 将GUI-Guider生成的代码目录拷贝一份放到vscode项目目录…...

AIP-165 按条件删除

编号165原文链接https://google.aip.dev/165状态批准创建日期2019-12-18更新日期2019-12-18 有时API需要提供一种机制&#xff0c;按照一些过滤参数删除大量资源&#xff0c;而非提供待删除的各资源名字。 这是一个稀有的场景&#xff0c;用于用户一次性删除数千或更多资源的…...

React Next项目中导入Echart世界航线图 并配置中文

公司业务要求做世界航线图&#xff0c;跑了三个ai未果&#xff0c;主要是引入world.json失败&#xff0c;echart包中并不携带该文件&#xff0c;源码的world.json文件页面404找不到。需要自己寻找。这是整个问题卡壳的关键点&#xff0c;特此贴出资源网址。 目录 一、安装 二…...

QT与网页显示数据公式的方法

一.网页中显示数学公式通常有三种主要方法 1.图片方式 原理&#xff1a;将公式转换为图片&#xff08;如 PNG、SVG&#xff09;&#xff0c;通过 <img> 标签嵌入网页。 实现步骤&#xff1a; 使用工具&#xff08;如 LaTeX dvipng、在线生成工具&#xff09;将公式渲…...

深入解析APP订阅页的运作机制(订阅页如何运作)

在当今数字经济的背景下&#xff0c;订阅模式已成为许多企业获取稳定收入的重要方式。无论是软件、视频流媒体还是电子商务&#xff0c;订阅服务都能为用户提供持续的价值体验。然而&#xff0c;如何有效地设计和运作一个订阅页&#xff0c;是决定用户是否愿意订阅的关键因素。…...

Golang倒腾一款简配的具有请求排队功能的并发受限服务器

golang官方指南[1]给了一些代码片段&#xff0c;层层递进演示了信道的能力: 1>. 信号量2>. 限流能力 var sem make(chan int, MaxOutstanding) func Serve(queue chan *Request) {for req : range queue {req: reqsem <- 1 go func() { // 只会开启MaxOutstandin…...