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

C++——string的了解和使用

引言

为了简化字符串的处理,C++标准库引入了string类。C++ 中的 std::string 是标准库提供的字符串类,用于处理可变长度的字符串。它封装了字符数组的操作,提供了安全、便捷的字符串处理方式。以下是关于 std::string 的详细介绍和使用指南。

一、基本概念

std::string 是 basic_string<char> 的 typedef,定义在 <string> 头文件中。它具有以下特点:

  • 动态管理内存:自动处理内存分配和释放,避免内存泄漏。
  • 安全的操作:边界检查、自动扩容等机制减少错误。
  • 丰富的接口:支持字符串拼接、查找、替换等常见操作。
  • 与 C 风格字符串兼容:可与 const char* 相互转换。 

下面是一个string的简单使用: 

int main()
{string s1;string s2("hello world");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;string s4(s2, 6, 15);cout << s4 << endl;string s5(s2, 6);cout << s5 << endl;string s6("hello world", 5);cout << s6 << endl;string s7(10, 'x');cout << s7 << endl;return 0;
}

开始之前我们先学习一下

auto和范围for
在学习string类之前,我们先来学习一下auto和范围for,方便我们后面的学习。 

C++11 引入的 auto 关键字和范围 for 循环(Range-based for loop)是两个非常实用的特性,它们共同提升了代码的简洁性和可读性。下面分别介绍这两个特性的用法和原理。 

一、auto 关键字

1. 基本用法

auto 用于自动推导变量的类型,由编译器根据初始化表达式的类型确定变量类型: 

auto x = 42;           // x 为 int 类型
auto d = 3.14;         // d 为 double 类型
auto s = "hello";      // s 为 const char* 类型
auto lst = {1, 2, 3};  // lst 为 std::initializer_list<int> 类型
2. 配合复杂类型

简化冗长的类型声明,例如迭代器: 

#include <vector>std::vector<int> vec = {1, 2, 3};// 传统写法
std::vector<int>::iterator it = vec.begin();// 使用 auto
auto it = vec.begin();  // it 为 std::vector<int>::iterator 类型
3. 配合引用和常量
const std::vector<int>& ref = vec;auto copy = ref;        // copy 是 std::vector<int>(顶层 const 被丢弃)
auto& ref2 = ref;       // ref2 是 const std::vector<int>&
const auto& cref = ref; // cref 是 const std::vector<int>&
auto* ptr = &vec;       // ptr 是 std::vector<int>*
4. 函数返回值推导

C++14 允许 auto 作为函数返回类型(需有明确的返回语句): 

auto add(int a, int b) {return a + b;  // 返回类型推导为 int
}
5. 注意事项
  • 必须初始化:auto 变量必须在定义时初始化。
  • 类型可能被调整:例如数组会退化为指针,顶层 const 可能被丢弃。
  • 避免滥用:在类型明确的场景(如 int x = 42)中无需使用 auto。 

 二、范围 for 循环

1. 基本语法

遍历可迭代对象(如数组、容器)的每个元素: 

for (declaration : expression) {// 循环体
}
  •  expression:表示可迭代对象(如 std::vector、数组)。
  • declaration:表示迭代变量,通常用 auto 声明。
2. 使用示例 
#include <vector>
#include <iostream>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 遍历并打印每个元素(值拷贝)for (auto x : vec) {std::cout << x << " ";}// 遍历并修改元素(使用引用)for (auto& x : vec) {x *= 2;}// 常量引用(只读访问)for (const auto& x : vec) {std::cout << x << " ";}// 遍历 C 风格数组int arr[] = {10, 20, 30};for (auto x : arr) {std::cout << x << " ";}// 遍历字符串std::string s = "hello";for (auto c : s) {std::cout << c << " ";}return 0;
}
3. 原理

范围 for 循环本质上是语法糖,等价于: 

auto&& __range = expression;
for (auto __it = std::begin(__range); __it != std::end(__range); ++__it) {declaration = *__it;// 循环体
}
  • 要求 expression 支持 std::begin() 和 std::end()(或成员函数 begin()/end())。 

三、auto 与范围 for 的结合使用 

1. 自动推导元素类型 

std::vector<std::pair<int, std::string>> vec = {{1, "one"}, {2, "two"}};// 使用 auto 自动推导 pair 类型
for (const auto& p : vec) {std::cout << p.first << ": " << p.second << std::endl;
}// C++17 结构化绑定
for (const auto& [num, str] : vec) {std::cout << num << ": " << str << std::endl;
}

2. 处理不同容器类型 

#include <map>std::map<int, std::string> m = {{1, "one"}, {2, "two"}};// 遍历 map(键值对)
for (const auto& pair : m) {std::cout << pair.first << ": " << pair.second << std::endl;
}// C++17 结构化绑定
for (const auto& [key, value] : m) {std::cout << key << ": " << value << std::endl;
}

四、注意事项

1. 避免意外拷贝

使用 auto& 或 const auto& 避免值拷贝: 

// 低效(每次迭代拷贝元素)
for (auto elem : large_vector) { ... }// 高效(引用避免拷贝)
for (const auto& elem : large_vector) { ... }
 2. 迭代器失效问题

在范围 for 中修改容器结构(如添加 / 删除元素)可能导致迭代器失效:

std::vector<int> vec = {1, 2, 3};// 错误:循环中删除元素会导致迭代器失效
for (auto& x : vec) {if (x == 2) {vec.erase(...);  // 危险!}
}

总结

  • auto 关键字:自动推导变量类型,简化复杂类型声明,提高代码可读性。
  • 范围 for 循环:简洁地遍历可迭代对象,避免手动管理迭代器。
  • 最佳实践:结合 auto 和引用(auto&/const auto&)高效遍历容器。 

标准库中的string类

我们Cplusplus的文档来辅助我们学习string: 

string类的接口汇总

1.string类的迭代器 

 C++ 的 std::string 类提供了迭代器(Iterator)来遍历字符串中的每个字符。迭代器是一种抽象的指针,允许以统一的方式访问容器(如 string、vector 等)中的元素。下面详细介绍 std::string 的迭代器。

 一、迭代器类型 std::string 支持以下几种迭代器:

1.正向迭代器:

  • string::iterator:可读写迭代器
  • string::const_iterator:只读迭代器(用于常量字符串)

2.反向迭代器:

  • string::reverse_iterator:可读写反向迭代器
  • string::const_reverse_iterator:只读反向迭代器
二、获取迭代器 

通过以下成员函数获取迭代器: 

函数描述返回类型
begin()指向第一个字符iterator/const_iterator
end()指向字符串末尾的下一个位置(空字符 '\0' 之后)iterator/const_iterator
rbegin()指向最后一个字符(反向开始)everse_iterator/const_reverse_iterator
rend()指向第一个字符的前一个位置(反向末尾)reverse_iterator/const_reverse_iterator
三、迭代器操作

迭代器支持以下基本操作:

  1. 解引用:*it 获取当前字符
  2. 递增 / 递减:++it、--it 移动迭代器
  3. 比较:it1 == it2、it1 != it2 判断迭代器是否相等 

四、使用示例 

1. 正向遍历字符串

#include <string>
#include <iostream>int main() {std::string s = "hello";// 使用普通迭代器(可修改)for (std::string::iterator it = s.begin(); it != s.end(); ++it) {*it = std::toupper(*it);  // 转为大写}// 使用常量迭代器(只读,推荐)for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {std::cout << *it;  // 输出: HELLO}// C++11 自动类型推导for (auto it = s.cbegin(); it != s.cend(); ++it) {std::cout << *it;  // 输出: HELLO}return 0;
}

 输出结果:

2. 反向遍历字符串 

std::string s = "hello";// 反向迭代器
for (std::string::reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit) {std::cout << *rit;  // 输出: olleh
}// C++11 自动类型推导
for (auto rit = s.rbegin(); rit != s.rend(); ++rit) {std::cout << *rit;  // 输出: olleh
}

输出结果:

3. 使用范围 for 循环(更简洁) 

std::string s = "hello";// 范围 for 本质上使用迭代器
for (char c : s) {std::cout << c;  // 输出: hello
}// 修改字符(使用引用)
for (char& c : s) {c = std::toupper(c);  // 转为大写
}
五、迭代器有效性
  • 插入 / 删除操作:在字符串中间插入或删除字符可能导致迭代器失效。
  • 重新分配内存:如果字符串容量不足,插入操作可能触发内存重新分配,导致所有迭代器失效。 

示例(危险操作): 

std::string s = "abc";
auto it = s.begin();s.insert(it, 'x');  // 插入后 it 可能失效
++it;  // 未定义行为!

总结

  • 迭代器类型:begin()/end()(正向)、rbegin()/rend()(反向),分 const 和非 const 版本。
  • 推荐写法:优先使用 auto 和范围 for 循环,减少手动管理迭代器。
  • 注意事项:避免在迭代过程中修改字符串结构,防止迭代器失效。 

2.string类的初始化和销毁

 string 是一个类,因此我们在初始化时肯定会调用其构造函数初始化。 

1.默认初始化

std::string str;  // 生成空字符串

2 .用字符串字面量初始化

std::string str = "Hello";  // 拷贝初始化
std::string str("Hello");   // 直接初始化

3.指定重复字符 

std::string str(5, 'a');  // 结果为 "aaaaa"

4.用已有字符串初始化 

std::string original = "World";
std::string copy(original);  // 拷贝构造

5.用部分字符串初始化 

std::string str("HelloWorld", 5);  // 取前5个字符,结果是 "Hello"

6.从迭代器范围初始化 

std::string source = "0123456789";
std::string str(source.begin() + 2, source.begin() + 5);  // 结果为 "234"

7.用初始化列表初始化 

std::string str = {'H', 'e', 'l', 'l', 'o'};

在C++中,std::string 是一个类模板的实例化,代表了一个可变的字符序列。由于它是一个类,当 std::string 对象的生命周期结束时(即离开其作用域),编译器会自动调用该对象的析构函数来清理其分配的资源。 

3.string类的容量操作 

在 C++ 中,std::string 的容量操作涉及内存管理和性能优化。理解这些操作可以帮助你避免不必要的内存重新分配,提高代码效率。以下是关于 std::string 容量操作的详细介绍: 

以下是比较常见的容量操作: 

函数名称功能
size返回字符串的有效长度
length返回字符串的有效长度
capacity返回字符串的容量大小
max size返回字符串的最大长度
clear清空字符串
empty检查是否为空串,是则返回true,否则返回false
reserve请求改变字符串的容量
resize重新设置有效字符的数量,超过原来有效长度则用c字符填充

 1.容量管理函数

reserve(size_t n)

  • 功能:请求字符串容量至少为 n 个字符,若当前容量不足则重新分配内存。
  • 用途:提前分配足够内存,避免多次小容量扩展导致的性能开销。
std::string str;
str.reserve(100);  // 预先分配100个字符的容量

resize(size_t n, char c = '\0')

功能:调整字符串长度为 n:

  • 若 n > 当前长度,则追加字符 c(默认补 '\0')。
  • 若 n < 当前长度,则截断字符串。 

注意:可能触发容量扩展。 

std::string str = "Hello";
str.resize(10, '!');  // str变为 "Hello!!!"

2.自动扩容机制

  • 当向字符串追加字符且长度超过当前容量时,std::string 会自动重新分配更大的内存空间。
  • 扩容策略通常是指数增长(如翻倍),以减少重新分配的频率。 
std::string str;
for (int i = 0; i < 100; ++i) {str += 'a';std::cout << "Length: " << str.length() << ", Capacity: " << str.capacity() << std::endl;
}
// 容量可能按15、31、63、127...的规律增长

3.容量与性能

  • 提前预留容量:若你预先知道字符串的大致长度,使用 reserve() 可避免多次扩容。 
std::string str;
str.reserve(1000);  // 避免循环中多次扩容
for (int i = 0; i < 1000; ++i) {str += 'a';
}
  • 避免过度扩容:频繁 resize() 或 += 可能导致多次内存分配,影响性能。 

 常见误区

1.混淆长度和容量: 

std::string str(10, 'a');  // 长度为10,容量至少为10
str.reserve(20);           // 长度仍为10,但容量变为20

2.误用 resize() 初始化: 

std::string str;
str.resize(5, 'a');  // 正确:初始化为 "aaaaa"
// 错误写法:std::string str(5, 'a');  // 这是构造函数,效果相同但语义不同

总结

  • 使用 reserve() 预分配内存以优化性能。
  • 使用 resize() 调整字符串长度,同时可能改变容量。
  • 自动扩容机制能减少分配次数,但可能导致内存浪费。 

4.string类的访问操作 

string类的访问操作通常有如下几种: 

函数名称功能
operator[]返回指定位置的字符,越界则报错
at返回指定位置的字符,越界则抛异常
back()返回字符串最后一个字符(不是"0”)
front()返回字符串第一个字符
substr()提取长度为 len 的子串。

 1. 索引访问

operator[]

  • 功能:通过下标访问指定位置的字符,不进行边界检查。
  • 返回值:char&(可修改)或 const char&(常量字符串)。
std::string str = "Hello";
str[0] = 'J';  // 修改首字符为 'J'
std::cout << str[1];  // 输出 'e'

at(size_t pos)

  • 功能:通过下标访问指定位置的字符,进行边界检查(越界时抛出 std::out_of_range 异常)。
  • 返回值:同 operator[]。 
try {std::cout << str.at(10);  // 抛出异常
} catch (const std::out_of_range& e) {std::cerr << "Error: " << e.what() << std::endl;
}

2. 迭代器访问

正向迭代器

  • 类型:iterator(可修改)和 const_iterator(只读)。
  • 示例: 
std::string str = "Hello";
for (auto it = str.begin(); it != str.end(); ++it) {std::cout << *it;  // 输出 'H', 'e', 'l', 'l', 'o'
}

反向迭代器

  • 类型:reverse_iterator 和 const_reverse_iterator。
  • 示例: 
for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {std::cout << *rit;  // 输出 'o', 'l', 'l', 'e', 'H'
}

3. 访问首尾字符 

front() 和 back()

  • 功能:直接访问字符串的第一个和最后一个字符(C++11 起)。
  • 返回值:char& 或 const char&。 
std::string str = "Hello";
str.front() = 'A';  // 修改首字符为 'A'
str.back() = '!';   // 修改尾字符为 '!'

4. 子串提取 

 substr(size_t pos = 0, size_t len = npos)

  • 功能:从位置 pos 开始提取长度为 len 的子串。
  • 返回值:新的 std::string 对象。
std::string str = "HelloWorld";
std::string sub = str.substr(6, 5);  // 提取 "World"
std::string all = str.substr();      // 提取整个字符串

5. 范围 for 循环(C++11 起)

功能:遍历字符串中的每个字符。 

for (char c : str) {std::cout << c;  // 输出 'H', 'e', 'l', 'l', 'o'
}// 修改字符(需使用引用)
for (char& c : str) {c = std::toupper(c);  // 转为大写
}

5.string类的修改操作 

在 C++ 中,std::string 提供了丰富的修改操作,包括追加、插入、删除、替换等。以下是详细介绍: 

1. 追加操作

operator+=

  • 功能:追加字符串、字符或初始化列表。 
std::string str = "Hello";
str += " World";         // 追加字符串
str += '!';             // 追加字符
str += {' ', 'C', '+'}; // 追加初始化列表

append()

  • 功能:追加多种类型的数据(字符串、子串、字符重复等)。 
str.append("!!!");                  // 追加字符串
str.append("abc", 2);               // 追加 "abc" 的前2个字符
str.append(3, 'X');                 // 追加3个 'X'
str.append({"a", "b", "c"});        // 追加初始化列表

push_back(char c)

  • 功能:在字符串末尾添加单个字符。 
str.push_back('!'); // 等价于 str += '!';

2. 插入操作 

insert()

  • 功能:在指定位置插入字符串、子串、字符等。 
std::string str = "HelloWorld";
str.insert(5, " ");           // 在位置5插入空格,结果:"Hello World"
str.insert(0, "Hi, ");        // 在开头插入字符串
str.insert(10, 3, '!');       // 在位置10插入3个 '!'

3. 删除操作

erase()

  • 功能:删除指定位置的字符或子串。 
str.erase(5, 1);             // 删除位置5的1个字符(空格)
str.erase(str.begin() + 5);  // 删除迭代器指向的字符
str.erase(str.begin(), str.begin() + 5); // 删除前5个字符

pop_back()  

  • 功能:删除字符串的最后一个字符(C++11 起)。 
str.pop_back(); // 移除 '!'

clear()

  • 功能:清空字符串,使其长度为 0。 
str.clear(); // str 变为 ""

4. 替换操作 

replace()

  • 功能:替换指定位置的子串。 
std::string str = "HelloWorld";
str.replace(5, 5, " C++");  // 从位置5开始的5个字符替换为 " C++"
str.replace(str.begin(), str.begin() + 5, "Hi"); // 前5个字符替换为 "Hi"

 5. 大小写转换

  • 需结合 <cctype> 中的函数:
#include <cctype>// 转为大写
for (char& c : str) {c = std::toupper(c);
}// 转为小写
for (char& c : str) {c = std::tolower(c);
}

6. 交换操作

swap()

  • 功能:交换两个字符串的内容,效率高(常数时间)。 

7. 修改注意事项

  1. 迭代器 / 引用失效:插入或删除操作可能导致迭代器、指针或引用失效。 
    auto it = str.begin();
    str.insert(it, 'A'); // it 失效,不应继续使用
  2.  内存重新分配:追加或插入可能触发容量扩展,导致内存重新分配。
    str.reserve(100); // 预先分配足够容量,避免频繁扩容
  3.  边界检查:确保插入或删除位置合法,否则可能导致未定义行为。
    if (pos < str.size()) {str.erase(pos, 1);
    }

    总结

操作类型常用函数
追加+=, append(), push_back()
插入insert()
删除erase(), pop_back(), clear()
替换replace()
大小写转换std::toupper(), std::tolower()
交换swap()

http://www.dtcms.com/a/269570.html

相关文章:

  • 将oracle表字段json字符串分解提取并返回单列表
  • Redis基础数据结构
  • 深度学习与图像处理 | 基于传统图像处理的自动驾驶车道线检测
  • XSLT注入与安全修复方法
  • 【快手】数据挖掘面试题0002:求某地铁站每日客流量,乘地铁经过、进出站人都包括在内
  • C#随机数生成全面详解:从基础到高级应用
  • 【ROS2 自动驾驶学习】03-ROS2常用命令
  • 网络安全护网实战:攻击手段解析与防御策略
  • 基于odoo17的设计模式详解---工厂模式
  • 阿里云mysql数据丢失,如何通过服务器备份在其他服务器上恢复数据,并获取mysql丢失数据,完成mysql数据恢复
  • Prompt Injection Attack to Tool Selection in LLM Agents
  • 深度剖析:向70岁老系统植入通信芯片——MCP注入构建未来级分布式通信
  • IP 能ping通,服务器是否开机?
  • Go语言反射机制详解
  • 基于ZYNQ7000的AD9226采集卡实现(3、PS LINUX DMA驱动实现)
  • vue3 el-table 行数据沾满格自动换行
  • 【debug】git clone 报错
  • Web前端: :is(通用选择器)
  • 图像轮廓检测与绘制:OpenCV 实战指南
  • claude code-- 基于Claude 4 模型的智能编程工具,重塑你的编程体验
  • 微软上线Deep Research:OpenAI同款智能体,o3+必应双王炸
  • Web后端开发-Mybatis
  • 玩转Docker | 使用Docker部署NotepadMX笔记应用程序
  • UDP的socket编程
  • unity 模型UV重叠问题相关(重新整理)
  • BUUCTF在线评测-练习场-WebCTF习题[GXYCTF2019]BabySQli1-flag获取、解析
  • 无法访问宝塔面板 - 特网科技
  • Coze智能体平台全景解析:从零构建企业级AI应用的实战指南
  • Spring Boot 企业项目技术选型
  • UI前端大数据可视化实战策略:如何设计符合用户认知的数据展示方式?