C++:STL-string

前言

本文主要介绍STL六大组件中的容器之一:string,在学习C++的过程中,我们要将C++视为一个语言联邦(摘录于Effective C++条款一)。如何理解这句话呢,我们学习C++,可将其分为四个板块;分别为C、Object-Oriented C++(面向对象的C++)、Template C++(模板)、STL。本文就介绍STL中的string

C语言字符串的局限性

C语言中的字符串处理依赖于字符数组和一系列标准库函数(如strcpystrcatstrlen等)。这些方法虽然基本能满足需求,但存在几个显著的缺点:

  1. 安全性问题:C语言中的字符串操作常常设计直接的内存操作问题,荣誉造成缓冲区的溢出、内存泄漏的等安全问题,例如strcpy函数在赋值数据时不会检查目标缓冲区的大小,这可能导致超出其容量的写操作。
  2. 效率低下:C语言的字符串操作通常需要手动管理字符串长度和内存,如需拼接字符串时,肯需要多次调用strlen来获取当前长度,然后再添加新内容,这样的操作效率较低。
  3. 使用不便:C语言的字符串需要程序员对内存非常小心谨慎的操作,容易出错且代码难以维护。

C++string类的优势

与C语言的基本字符串处理相比,C++的string类提供了一个更安全、更高效、更易用的字符串操作方式:

  1. 自动内存管理:string类自动管理内存,使用者不需要关心内存分配和释放的问题,极大地降低了内存泄漏和缓冲区溢出的风险。
  2. 丰富的成员函数:string类内置了大量的成员函数,如append, find, replace, substr等,使得字符串的处理更为方便和直观。
  3. 动态大小:string对象可以根据需要动态增长和缩小,无需预先声明最大容量,这一点与静态大小的C语言字符数组形成鲜明对比。
  4. 操作符重载和迭代器支持:string类重载了多个操作符(如+、=等),支持迭代器,使得字符串操作更符合C++的对象操作习惯,也支持现代C++中的范围for循环和算法库函数。
  5. 兼容性和灵活性:尽管string以字节为单位进行操作,对于多字节字符集的支持可能有限,但它在大多数情况下能够兼容处理UTF-8等编码,尤其是在使用支持这些编码的库时。

string类

C++ 中提供了专门的头文件string(注意不是 string.h,这个是C风格字符串相关函数的头文件),来支持string类型。string类定义隐藏了字符串的数组性质,让我们可以像处理普通变量那样处理字符串。string对象和字符数组之间的主要区别是:可以将string对象声明为简单变量,而不是数组

标准库中的string类文档介绍

1.字符串时表示字符序列的类。

2.标准的字符串类提供了对此类对象的支持,其接口累死与标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。

3.string类是使用char(即作文他的字符类型,使用他的默认char_traits和分配器类型)

4.string类是basic_string模版类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作文basic_string分默认参数(根于更多漫步信息参考basic_string)。

5.注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(符UTF-8)的序列,这个类的缩影成员(如长度或大小)已经它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

字符串作为字符序列

string类本质上是一个用于表示字符序列的容器。它封装了字符数组的许多复杂操作,提供了一种更安全、更直观的方式来处理文本数据。用户不需要关心底层的字符数组和内存管理,所有这些都由string类自动处理。

接口与标准容器

string类的接口设计借鉴了标准库中STL容器的模式,例如vector和deque。这意味着string提供了类似于这些容器的多种成员函数,如迭代器的支持、元素访问、修改操作、容量查询等。这种设计使得string类即熟悉又易于使用对于已经熟悉其他C++标准容器的开发者。

string类的常用接口说明

string类对象的常见构造

(constructor)函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(size_t n,char c)string类对象中包含n个字符c
string(const string& s)拷贝构造函数
string s1;              // 构造空的string类对象s1
string s2("hello bit"); // 用C格式字符串构造string类对象s2
string s3(s2);          // 拷贝构造s3

string类对象的容量操作

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检查字符串释放为空串,是返回true,否返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出的空间用字符c填充

size()和length():

size_t size() const;
size_t length() const;
std::string str = "Hello, world!";
std::cout << "Length of string: " << str.length() << std::endl; // 输出 13
std::cout << "Size of string: " << str.size() << std::endl;     // 同样输出 13

这两个函数都返回字符串当前的长度,即字符串中字符的数量,这两种并无相异,忧郁string出现的较早原因,当时没有STL其他容器,先出现了length,后来为了统一接口,与其他容器接口保持一致,因此出现了size。

capacity():

size_t capacity() const;

std::string str = "Test";
std::cout << "Capacity of string:" << str.capacity() << std::endl; // 输出分配的内存大小

capacity()函数返回为字符串分配的存储空间的大小,通常这个值大于或等于size()。这可以给出底层数组的空间大小,有助了解内存的使用情况。

empty:

bool empty() const;
std::string str;
std::cout << "Is the string empty? " << (str.empty() ? "Yes" : "No") << std::endl; // 输出 'Yes'

empty()函数检查字符串是否为空(即长度为0)。如果字符串为空,返回true,否则返回false。

clear:

void clear() noexcept;
std::string str = "Something";
str.clear(); // 清空字符串
std::cout << "String after clear: '" << str << "'" << std::endl; // 输出 ''

clear()函数删除字符串中的所有字符,使其长度变为0,这个函数不会改变容量的大小。

reserve:

void reserve(size_t n = 0);
std::string str;
str.reserve(100); // 为存储至少100个字符预分配内存
std::cout << "New capacity after reserve: " << str.capacity() << std::endl;

reserve()函数试图改变字符串的容量,即预先分配足够的内存来存储至少n个字符,避免多次增加字符串大小时的重复分配。

resize:

void resize(size_t n);
void resize(size_t n, char c);
std::string str = "Hello";
str.resize(10, 'x'); // "Helloxxxxx"
str.resize(3);       // "Hel"

resize()函数更改字符串的长度,如果新的长度大于当前长度,新位置将于字符c填充(如果提供了c的话)。如果新的长度小于当前长度,多余的字符将被截断,空间大小不会被改变。

string类的访问及遍历操作

在std::string类中,访问和遍历字符串的方法包括使用下标操作符、迭代器和范围for循环。这些方法提供了灵活的方式来访问和遍历字符串中的字符。

函数名称功能说明
operator[]返回pos位置的字符,const string类对象调用
begin + endbegin获取一个字符的迭代器+end获取最后一个字符下一个位置的迭代器
rbegin + rend反向迭代器rbegin获取一个字符的迭代器+rend获取最后一个字符下一个位置的迭代器
范围forC++11支持更简介的范围for的新遍历方式

operator[]:

std::string str = "Hello, world!";
char ch1 = str[0];  // 访问第一个字符,ch1 = 'H'
char ch2 = str[7];  // 访问第八个字符,ch2 = 'w'std::cout << "First character: " << ch1 << std::endl;
std::cout << "Eighth character: " << ch2 << std::endl;// 修改字符串中的字符
str[5] = '!';
std::cout << "Modified string: " << str << std::endl;  // 输出 "Hello! world!"

下标操作符operator[]允许你访问字符串中的特定位置的字符,这种访问方式是随机访问,时间复杂度为O(1)。

迭代器:

std::string str = "Hello";
std::cout << "Characters in string: ";// 使用正向迭代器遍历字符串
for (auto it = str.begin(); it != str.end(); ++it) {std::cout << *it << ' ';
}
std::cout << std::endl;// 使用反向迭代器遍历字符串
std::cout << "Characters in reverse: ";
for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {std::cout << *rit << ' ';
}
std::cout << std::endl;

迭代器提供了一种方式按顺序访问容器中的每个元素。std::string支持正向和反向迭代器

范围for:

std::string str = "world";std::cout << "Characters in string using range-for: ";
for (char c : str) {std::cout << c << ' ';
}
std::cout << std::endl;// 修改字符串中的字符(需要使用引用)
for (char &c : str) {c = toupper(c);  // 将字符转换为大写
}
std::cout << "Modified string: " << str << std::endl;  // 输出 "WORLD"

范围for循环是C++11引入的一个特性,它允许简介的变量容器中的所有元素,在使用范围for循环遍历std::string时,可以直接访问每个字符。

string类对象的修改操作

类提供了多种方法来修改字符串的内容,这些方法包括添加、删除、插入和替换字符等操作,下面这些方法的详细介绍和使用实例。

函数名称功能说明
push_back在字符串后尾插入字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回c格式字符串
find + npos从字符串pos位置开始往后开始找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往后开始找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

push_back:

void push_back(char c);
std::string str = "Hello"; 
str.push_back('!'); 
std::cout << str << std::endl; // 输出 "Hello!"

push_back将一个字符追加到字符串的末尾。

append:

string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);
string& append(const char* s);
string& append(const char* s, size_t n);
string& append(size_t n, char c);
std::string str = "Hello";
str.append(" world", 6);  // 附加了 " world" 中的前6个字符
std::cout << str << std::endl;  // 输出 "Hello world"

append方法添加字符串、字符数组或多个相同字符到现有字符串的末尾。

operator+=:

void operator+=(const string& str);
void operator+=(char c);
void operator+=(const char* s);
std::string str = "Hello";
str += ", world!";
std::cout << str << std::endl;  // 输出 "Hello, world!"

operator+=用于将字符串、字符或者c字符串附加到现有的std::string对象上。

c_str:

int main()
{string s1("hello world");cout << s1.c_str() << endl;cout << s1.data() << endl;return 0;
}

c_str返回string类中存储字符串的字符指针,在C语言中字符串是以'\0'结尾的一些字符的集合。但在C++中,string类不以'\0'结尾,而是根据有效空间的大小结束。本质作用是:将const string* 类型转化为const char* 类型。

find和npos:

// 从string拷贝给字符数组
// size_t copy (char* s, size_t len, size_t pos = 0) const;
char arr[] = "hello world";
string s1("xxxxxxxxxxxxxxxx");
s1.copy(arr, 6, 2);
cout << s1 << endl;
// 寻找某个字符串的起始位置
// size_t find (const string& str, size_t pos = 0) const;
string tmp("abc");
string s2("abbadabcdeabcd");
size_t pos1 = s2.find(tmp, 0);
// 从后往前找
//size_t rfind (const string& str, size_t pos = npos) const;
size_t pos2 = s2.rfind(tmp, s2.size() - 1);
cout << pos1 << endl;
cout << pos2 << endl;

find有很多函数重载,学会一种就可以了,size_t find(const string& s, int pos = 0);s是需要查找的字符串,pos是从那个位置查找,如果未找到则返回npos(npos表示size_t 的最大值)。

rfind:

string s1("fghasdabc");
cout << s1.rfind('a') << endl;string s2("abc");
cout << s1.rfind(s2) << endl;
cout << s1.rfind('e') << endl;

rfind和find功能是一样的,不过一个是往前找,一个向后找的区别。

substr:

string s1("Hello World");cout << s1 << endl;
string s2(s1.substr(0, 5));cout << s1 << endl;
cout << s2 << endl;

在string中的pos位置截取n个字符,然后返回。

string类的深拷贝与浅拷贝问题

浅拷贝:

在类中如果用户没有显示实现而是由编译器自动生成的成员函数叫做默认成员函数,这样的成员函数有六个。默认成员函数中的拷贝构造函数和赋值运算符重载函数会以逐字节的方式将原对象的内容原封不动的拷贝或赋值给新的对象,如果对象中管理资源,最后就会导致多个对象共用一份资源,当其中一个对象销毁时会将该资源释放掉,其他对象再想操作该资源时就会发生访问违规,这便是浅拷贝。

namespace qx
{class string{public:string(const char* s=""){if (nullptr == s){cout << "string():false" << endl;return;}char* ptr = new char[strlen(s) + 1];strcpy(_str, s);}//........private:char* _str;};
}int main()
{qx::string s1("hello world!");qx::string s2(s1);return 0;
}

由于类对象销毁时调用析构函数,会先将s2对象销毁,再调用析构进行销毁s1对象,但是因为拷贝构造是浅拷贝,导致s1和s2指向同一块内存空间,销毁掉s2之后内存空间被释放,s1找不到改内存空间,最终违规访问。

深拷贝:

如果一个类中涉及到资源管理,其拷贝构造函数、赋值运算符重载以及析构函数都必须要显示给出,一般情况都是按照深拷贝方式提供。

//显示实现拷贝构造
string(const string& str):_str(nullptr)
{string strTmp(str._str);swap(_str, strTmp._str);
}//显示实现赋值运算符重载
string& operator=(string& str)
{if (this != &str){string strTmp(str);swap(_str, strTmp._str);}
}//显示实现析构函数
~string()
{if (_str){delete[] _str;_str = nullptr;}
}

深拷贝即给每个对象独立分配资源,保证多个对象之间不会因为共享资源而造成多次释放导致程序崩溃的问题。

string总结

深入探讨C++标准库中的string类,从其构造函数、容量操作函数,到访问及遍历操作,以及类对象的修改操作。string类作为C++中处理字符串的核心工具,提供了丰富的接口来高效、安全地管理和操作字符串。掌握这些功能不仅可以提高编程效率,还能帮助开发者编写更加健壮和可维护的代码,有效地处理现代软件开发中的文本数据挑战。

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

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

相关文章

7天精通Web APIs——正则阶段案例(理论+实战)(第六天)

正则表达式的定义和使用 定义&#xff1a;是一种匹配模式&#xff0c;用于匹配字符串中字符组合 作用&#xff1a;表单验证&#xff08;匹配&#xff09;、过滤敏感词&#xff08;替换&#xff09;、字符串中提取我们想要的部分&#xff08;提取&#xff09; 使用分为两步&…

AI翻唱+视频剪辑全流程实战

目录 一、AI翻唱之模型训练 &#xff08;1&#xff09;模型部署 &#xff08;2&#xff09;数据集制作——搜集素材 &#xff08;3&#xff09;数据集制作——提升音频质量 方法一&#xff1a;使用RVC提供的音频处理功能。 方法二&#xff1a;可以使用音频剪辑工具Ad…

如何开通微软必应bing国内竞价广告账户?

微软必应Bing作为全球第二大搜索引擎&#xff0c;凭借其特有的用户群和市场定位&#xff0c;为中国广告主开辟了一片潜力无限的蓝海市场。云衔科技通过专业实力&#xff0c;为广告主提供全方位支持&#xff0c;从开户到代运营&#xff0c;助力企业扬帆起航。 一、微软必应bing…

计算机网络(第八版 谢希仁 编著) 期末复习大纲

一.每章总结 第一章&#xff1a;分组交换&#xff0c;计网定义、范围划分&#xff0c;性能指标&#xff0c;五层体系结构&#xff0c;TCP/IP体系结构 第二章&#xff1a;物理层&#xff0c;码元&#xff0c;基带调制(数字信号->数字信号&#xff0c;也叫编码)&#xff0c;带…

yum、rpm相关命令-工具手册

1.rpm文件: 1.1安装rpm文件: rpm -ivh kde-select.rpm23 #--nodeps强制安装,无视环境缺少依赖的检查 rpm -ivh --nodeps kde-select.rpm #--force --replacefiles可以无视rpm的冲突去强制替换(如两个rpm的安装路径相同了会有冲突) rpm -ivh --nodeps --force --replacef…

JUC下的ThreadLocalRandom详解

ThreadLocalRandom 是Java并发包&#xff08;java.util.concurrent&#xff09;中提供的一个随机数生成器类&#xff0c;它是从Java 7开始引入的。相较于传统的Math.random()或Random类&#xff0c;ThreadLocalRandom更适用于多线程环境&#xff0c;因为它为每个线程维护了一个…

前端css中线性渐变(linear-gradient)的使用

前端css中线性渐变 一、前言二、关键词句三、主要内容说明&#xff08;一&#xff09;、线性渐变方向1.角度调整方向2.负值角度&#xff0c;源码13.源码1运行效果4.关键字调整方向5.to right向右线性渐变&#xff0c;源码26.源码2运行效果 &#xff08;二&#xff09;、线性渐变…

八、e2studio VS STM32CubeIDE之内存使用情况窗口

目录 一、概述/目的 二、STM32CubeIDE Build Analyzer 三、e2studio Memory Usage 八、e2studio VS STM32CubeIDE之内存使用情况窗口 一、概述/目的 1、嵌入开发最大特点之一就是资源受限&#xff0c;关注芯片资源使用详情是优秀工程师的技能之一 2、Keil和IAR都不支持内存…

杨校老师项目之基于大数据技术栈hadoop商业web应用的日志分析系统

获取全套资料&#xff1a; 有偿获取&#xff1a;mryang511688 摘要&#xff1a; 互联网世界的先驱者们一致认为大数据将是未来互联网产业&#xff0c;甚至是整个人类各个产业的基础资源&#xff0c;那么到底什么是大数据&#xff0c;大数据给我们的世界是如何带来变化的呢&am…

【2024年5月备考新增】】 考前篇(1)《官方平台 - 考生模拟练习平台操作指南》

1 登录 登录中国计算机技术职业资格网(https://www.ruankao.org.cn),点击服务园地的【模拟练习】。 温馨提示:实名认证通过且注册成功的考生方可登录模拟练习。 2 下载模拟作答系统 温馨提示: 点击“下载”按钮,下载对应的模拟作答系统。未报名成功的考生不允许下载…

分布式与一致性协议之POW算法

POW算法 概述 谈起比特币&#xff0c;你应该并不陌生。比特币是基于区块链实现的&#xff0c;而区块链运行在因特网上&#xff0c;这就存在有人试图作恶的情况。有些读者可能已经发现了&#xff0c;口信消息型拜占庭问题之解、PBFT算法虽然能防止坏人作恶&#xff0c;但只能防…

第十四天:PHP 开发,输入输出类留言板访问 IPUA 头来源

1.PHP-全局变量$_SERVER 2.MYSQL-插入语法INSERT 3.输入输出-XSS&反射&存储 4.安全问题-XSS跨站&CSRF等 1.输入输出类安全问题 反射性xss 这个先准备一个数据&#xff0c;随便弄一个表名字&#xff0c;在随便弄一点数据存入即可 作为连接的数据库&#xff0c…