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

C++:String类

文章目录

  • 前言
  • 一、auto和范围for
  • 二、string常用接口
  • 三、浅拷贝与深拷贝
  • 总结


前言

掌握String类意味着能:

安全高效地处理文本;

避免底层编码陷阱;

利用高级功能(如正则表达式);

理解内存机制,优化性能。

无论是开发应用、算法实现还是系统设计,字符串操作都是必备技能,直接关系到程序的健壮性和效率。


一、auto和范围for

1.1 auto关键字

auto关键字用于声明变量时自动推导类型,无需显式指定类型。

主要目的:减少冗长的类型声明,尤其在处理复杂类型(如迭代器、模板或lambda表达式)时,使代码更简洁。

推导规则:编译器根据初始化表达式推断类型,类似于模板参数推导。例如:如果初始化表达式是整数,auto推导为int。如果表达式是字符串字面量,推导为const char*。

注意:

1.用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
2.当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
3.auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
4.auto不能直接用来声明数组
#include<iostream>
using namespace std;
int func1()
{
return 10;
}
// 不能做参数
void func2(auto a)
{}
// 可以做返回值,但是建议谨慎使用
auto func3()
{
return 3;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = func1();
// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
auto e;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
int x = 10;
auto y = &x;
auto* z = &x;
auto& m = x;
cout << typeid(x).name() << endl;
cout << typeid(y).name() << endl;
cout << typeid(z).name() << endl;
auto aa = 1, bb = 2;
// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto cc = 3, dd = 4.0;
// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] = { 4, 5, 6 };
return 0;
}
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange",
"橙子" }, {"pear","梨"} };
// auto的用武之地
//std::map<std::string, std::string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
return 0;
}

1.2范围for

范围for循环的核心思想是自动遍历一个序列(如列表、数组、字符串等)中的每个元素。

它抽象了底层迭代过程:循环变量在每次迭代中绑定到序列的当前元素。

例如,在遍历一个列表时,循环变量会依次取列表中的每个值。

优点:

代码更简洁:减少索引管理代码。

可读性更强:直接表达“遍历每个元素”的意图。

安全性更高:避免索引越界错误。

范围for的底层,容器遍历实际就是替换为迭代器

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
// C++98的遍历
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
cout << array[i] << endl;
}
// C++11的遍历
for (auto& e : array)
e *= 2;
for (auto e : array)
cout << e << " " << endl;
string str("hello world");
for (auto ch : str)
{
cout << ch << " ";
}
cout << endl;
return 0;
}

二、string常用接口

2.1 成员类型

实际底层为 char* str,_size, _capacity 的顺序表

2.2 成员函数

与顺序表数据结构类似,除了基本的增删查改,还多了一下这些内容

2.2.1 构造析构与复制重载

构造函数

析构函数

赋值运算符重载

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

2.2.2 迭代器

迭代器是C++标准库中提供的一种抽象,用于访问容器(vectorlistmap)中的元素,而无需关心容器的内部实现细节。它的作用类似于指针,允许遍历和操作容器中的元素

C++迭代器分为以下五类,每种类型支持不同的操作:

  1. 输入迭代器(Input Iterator):只能单向读取数据(如istream_iterator)。
  2. 输出迭代器(Output Iterator):只能单向写入数据(如ostream_iterator)。
  3. 前向迭代器(Forward Iterator):支持读写和单向遍历(如forward_list的迭代器)。
  4. 双向迭代器(Bidirectional Iterator):支持双向遍历(如listmap的迭代器)。
  5. 随机访问迭代器(Random Access Iterator):支持随机访问和算术运算(如vectorarray的迭代器)。

2.2.3 对容量操作

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不会改变容量大小。

2.2.4 访问元素

2.2.5 修改

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

2.2.6 字符串操作

#include <string>
using namespace std;string str1 = "Hello";
string str2("World");
string str3 = str1 + " " + str2;string s = "example";
char c1 = s[2];    // 'a'
char c2 = s.at(3); // 'm'for (size_t i = 0; i < s.size(); ++i) {cout << s[i];
}
for (char ch : s) {cout << ch;
}string s = "C++";
s.append(" Programming"); // "C++ Programming"
s.insert(3, " is");       // "C++ is Programming"
s.erase(6, 5);            // "C++ Programming"
s.replace(4, 11, "awesome"); // "C++ awesome"string s1 = "apple";
string s2 = "banana";if (s1 < s2) {cout << "s1 is less than s2";
}
int result = s1.compare(s2); // returns <0, 0, or >0string s = "Hello, world!";
size_t pos = s.find("world"); // 7
pos = s.find('o');            // 4
pos = s.rfind('o');           // 8
pos = s.find_first_of("aeiou"); // 1 ('e')string s = "Hello, world!";
string sub = s.substr(7, 5); // "world"string numStr = "123";
int num = stoi(numStr);    // 123
string strNum = to_string(456); // "456"string input;
cin >> input; // reads until whitespace
getline(cin, input); // reads entire line
cout << "You entered: " << input;string s = "example";
const char* cstr = s.c_str();
const char* data = s.data(); // C++17起保证以空字符结尾

三、浅拷贝与深拷贝

3.1 浅拷贝(Shallow Copy)

浅拷贝仅复制对象的成员值,包括指针的地址,而不复制指针指向的实际数据。这可能导致多个对象共享同一块内存,引发问题如内存泄漏或双重释放错误。

  • 工作原理:如果类使用默认的拷贝构造函数或赋值运算符,C++会执行浅拷贝。指针成员被直接复制,新旧对象指向同一内存地址。
  • 潜在风险:当其中一个对象修改数据或析构时,会影响其他对象,导致未定义行为。
  • 代码示例: 以下是一个简单的类ShallowClass,包含一个指针成员。默认拷贝行为是浅拷贝。

3.2 深拷贝(Deep Copy)

深拷贝不仅复制对象成员,还复制指针指向的实际数据,创建独立的内存副本。这确保了对象间的数据隔离,避免共享内存问题。

  • 工作原理:需要自定义拷贝构造函数和赋值运算符,在拷贝时手动分配新内存并复制内容。
  • 优点:安全、可靠,适用于动态资源管理。
  • 代码示例: 修改上述类为DeepClass,实现深拷贝。
#include <iostream>
using namespace std;class DeepClass {
public:int* data;DeepClass(int value) {data = new int(value);}// 自定义拷贝构造函数(深拷贝)DeepClass(const DeepClass& other) {data = new int(*other.data); // 分配新内存并复制值}// 自定义赋值运算符(深拷贝)DeepClass& operator=(const DeepClass& other) {if (this != &other) { // 避免自赋值delete data; // 释放现有内存data = new int(*other.data); // 分配新内存并复制值}return *this;}~DeepClass() {delete data; // 安全释放}
};int main() {DeepClass obj1(10);DeepClass obj2 = obj1; // 深拷贝:obj2.data 指向新内存副本*obj1.data = 20; // 修改 obj1cout << *obj1.data << endl; // 输出 20cout << *obj2.data << endl; // 输出 10,obj2 数据独立,未受影响// 析构时各自释放内存,无错误return 0;
}//在这个例子中,拷贝构造函数和赋值运算符显式创建新内存并复制值,确保obj1 和 obj2 完全独立。


总结

C++标准库中的string类是一个用于表示和操作字符串的类,它位于头文件<string>中。string类提供了高效、安全且易于使用的字符串处理功能,避免了C风格字符串(如char*)的常见问题(如缓冲区溢出)。以下是其主要特性的总结:

1. 主要特性
  • 动态内存管理:string对象自动管理内存,无需手动分配或释放。例如,字符串长度可以动态变化。
  • 丰富的成员函数:支持各种字符串操作,如查找、替换、连接和比较。
  • 运算符重载:使用运算符简化操作,例如:
    • + 用于字符串连接(如 str1 + str2)。
    • ==!=<> 等用于比较。
  • 兼容性:与C风格字符串无缝交互,例如可通过c_str()方法获取const char*
  • 安全性:内置边界检查,减少运行时错误。
2. 常用成员函数
  • 长度相关
    • size() 或 length():返回字符串长度(类型为size_t),例如长度可表示为 $n$。
    • empty():检查字符串是否为空。
  • 子串操作
    • substr(pos, len):从位置 pos 提取长度为 len 的子串。
    • find(str):查找子串 str 的起始位置,返回索引(从0开始)。
  • 修改操作
    • append(str):在末尾添加字符串。
    • insert(pos, str):在指定位置插入字符串。
    • replace(pos, len, str):替换部分字符串。
  • 其他
    • clear():清空字符串。
    • at(index):访问指定位置的字符(带边界检查)。
3. 优点
  • 易用性:简化字符串处理,代码更简洁。
  • 高效性:内部使用动态数组,支持快速操作。
  • 类型安全:强类型设计,减少错误。
  • 可扩展性:支持Unicode和多字节字符(通过wstring等变体)。
#include <iostream>
#include <string>
using namespace std;int main() {string str = "Hello, C++"; // 创建字符串cout << "Original: " << str << endl;cout << "Length: " << str.length() << endl; // 输出长度string sub = str.substr(0, 5); // 提取子串 "Hello"cout << "Substring: " << sub << endl;str.append(" World!"); // 添加字符串cout << "After append: " << str << endl;return 0;
}
5. 使用建议
  • 在C++中优先使用string而非C风格字符串。
  • 注意:string类在C++11及以后版本中支持移动语义,提升性能。
  • 对于复杂操作,可结合STL算法(如<algorithm>中的函数)。

总之,string类是C++中处理字符串的核心工具,它提升了代码的可靠性和可维护性。

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

相关文章:

  • 金华网站开发杭州自适应网站建设
  • ROS (无人机、机器人)与外部系统对接
  • 苏州市吴江住房和城乡建设局网站书籍网站设计
  • Pytorch工具箱2
  • 物业网站开发wordpress英文博客模板下载
  • 光影(1)
  • iOS 混淆与机器学习模型保护 在移动端保密权重与推理逻辑的实战指南(iOS 混淆、模型加密、ipa 加固)
  • Axios的快速入门
  • 网站建设品牌公司排名网页游戏4399在线游戏
  • 木渎建设局网站哪个网站可以做加工代理的
  • 培训班小程序模板如何一键套用,分享微信小程序的制作方法
  • 陕西做天然气公司网站网站如何做的有特色
  • 娱乐网站的代理怎么做WordPress 短码转换
  • Unity - Spine
  • Spring依赖注入方式
  • 宏升温岭网站建设扁平化网站源码
  • 网站页面架构图网站定制开发 广州
  • 做html网站搜索框代码网站关键词如何做优化
  • Java 中配置 Selenium UI 自动化测试 并生成 Cucumber 报告
  • SAP中BAPI_MATERIAL_SAVEDATA分析和使用
  • 云手机能否稳定的运行传奇游戏
  • 做网站应该先从什么地方开始西宁seo快速排名
  • Tomcat 相关漏洞扫描器(二)
  • PyTorch张量切片的陷阱:视图与副本
  • 旅游酒店网站建设零基础可以学平面设计吗
  • 哪里学网站开发店铺设计图片
  • 小淇云库-Docker 新手入门:容器创建、镜像打包与数据卷管理基础
  • Docker 部署 Ollama 详细教程以及镜像高速下载地址
  • Rust数组与向量
  • 【LeetCode 每日一题】1886. 判断矩阵经轮转后是否一致