C++:STL-string

news2024/11/18 17:49:00

前言

本文主要介绍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.coloradmin.cn/o/1657736.html

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

相关文章

基于springboot实现医院药品管理系统项目【项目源码+论文说明】

基于springboot实现医院药品管理系统演示 摘要 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0c;而读书就…

python-类和对象

1、设计一个 Circle类来表示圆,这个类包含圆的半径以及求面积和周长的函数。再使用这个类创建半径为1~10的圆,并计算出相应的面积和周长。 &#xff08;1&#xff09;源代码&#xff1a; import math class Circle: def __init__(self, r): self.r r #面积 def area(self): r…

嵌入式开发九:STM32时钟系统

时钟对于单片机来说是非常重要的&#xff0c;它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行。时钟系统犹如人的心脏&#xff0c;一旦有问题整个系统就崩溃。我们知道 STM32 属于高级单片机&#xff0c;其内部有很多的外设&#xff0c;但不是所有外设都使用同一时…

IO 5.9号

创建一对父子进程&#xff1a; 父进程负责向文件中写入 长方形的长和宽 子进程负责读取文件中的长宽信息后&#xff0c;计算长方形的面积 #include <myhead.h>int main(int argc, const char *argv[]){int retvalfork();if(retval>0){float length,width;int wfdopen(…

【二维数组】

目录 作业 对比&#xff1a; 结果&#xff1a; 二维数组 二维数组的初始化 作业 作业 #define max(a,b)(a>b)?a:b #include<stdio.h> int main() {int x, y,c;scanf("%d %d", &x,&y);cmax(x, y);printf("%d", c);return 0; } 对比…

关于模型参数融合的思考

模型参数融合通常指的是在训练过程中或训练完成后将不同模型的参数以某种方式结合起来&#xff0c;以期望得到更好的性能。这种融合可以在不同的层面上进行&#xff0c;例如在神经网络的不同层之间&#xff0c;或者是在完全不同的模型之间。模型参数融合的目的是结合不同模型的…

震惊,现在面试都加科技与狠货了

震惊&#xff0c;现在面试都加科技与狠货了 生成式AI盛行的现在&#xff0c;程序员找工作变容易了吗我和老痒喝着大酒&#xff0c;吃着他的高升宴&#xff0c;听他说他面试的各种细节&#xff0c;老狗我只恨自己动作慢了一步&#xff0c;不然现在在那侃侃而谈的就是我了。 面试…

【深度学习】【Lora训练2】StabelDiffusion,Lora训练过程,秋叶包,Linux,SDXL Lora训练

文章目录 一、如何为图片打标1.1. 打标工具1.1.1. 秋叶中使用的WD1.41.1.2. 使用BLIP21.1.3. 用哪一种 二、 Lora训练数据的要求2.1 图片要求2.2 图片的打标要求 三、 Lora的其他问题qa1qa2qa3qa4qa5 四、 对图片的处理细节4.1. 图片尺寸问题4.2. 图片内容选取问题4.3. 什么是一…

深入浅出,一文搞懂向量数据库工作原理和应用

大家好&#xff0c;在今天这个数据复杂性日益增长和高维信息丰富的时代&#xff0c;传统数据库在高效处理和提取复杂数据集方面已显得捉襟见肘。向量数据库&#xff0c;作为一项应运而生的技术创新&#xff0c;成功解决了数据领域在不断扩展过程中所面临的挑战。 1.向量数据库…

常见的一些RELAXED MODEL CONCEPTS

释放一致性(release consistency, RC) RC的核心观点是&#xff1a;使用 FENCE 围绕所有同步操作是多余的 同步获取 (acquire) 只需要一个后续的 FENCE&#xff0c;同步释放 (release) 只需要一个前面的 FENCE。 对于表 5.4 的临界区示例&#xff0c;可以省略 FENCE F11、F14…

Vue3专栏项目 -- 一、第一个页面(下)

一、Dropdown 组件&#xff08;下拉菜单组件&#xff09;编码 1、基本功能&#xff1a;展示出下拉按钮和下拉菜单栏的样式 我们可以通过bootstrap来实现这个下拉框&#xff0c;需要注意它这个只是有样式&#xff0c;是没有行为的 然后这个下拉按钮的文字展示是根据用户名称展…

洗地机什么品牌好?洗地机怎么选?618洗地机选购指南

随着科技的飞速发展&#xff0c;洗地机以其高效的清洁能力、稳定的性能和用户友好的设计而闻名&#xff0c;不仅可以高效吸尘、拖地&#xff0c;还不用手动洗滚布&#xff0c;已经逐渐成为现代家庭不可或缺的清洁助手。然而&#xff0c;在众多品牌和型号中&#xff0c;如何选择…

Python专题:七、函数初探

代码的重用,重复的机械性功能 封装性,不用了解其组成原理 易于维护,更新 def是关键词,函数定义,add3函数名(自定义)三个数相加,a,b,c是函数的形式参数,需要注意的是,在出现三个点号之后,还需再输入一个回车,出现三个尖括号,才算函数定义完成,定义完之后就可以使…

MySQL 通过 systemd 启动时 hang 住了……

mysqld&#xff1a;哥&#xff0c;我起不来了…… 作者&#xff1a;贲绍华&#xff0c;爱可生研发中心工程师&#xff0c;负责项目的需求与维护工作。其他身份&#xff1a;柯基铲屎官。 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编…

网工内推 | 技术支持工程师,最高15k,加班有补贴

01 星网信通 招聘岗位&#xff1a;售前技术支持 职责描述&#xff1a; 1、售前技术支持&#xff1a;技术交流、产品选型报价、方案制作等工作&#xff1b; 2、招投标支持&#xff1a;项目招标参数撰写、标书质疑、应标文件技术部分撰写及资质文件归纳准备、现场讲标及技术澄清…

95、动态规划-编辑距离

递归暴力解法 递归方法的基本思想是考虑最后一个字符的操作&#xff0c;然后根据这些操作递归处理子问题。 递归函数定义&#xff1a;定义一个递归函数 minDistance(i, j)&#xff0c;表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最小操作数。 递归终止条件…

命运交织的节点:分布式事务最终一致性的心跳共鸣纪实

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 在当今云计算和微服务架构大行其道的时代&#xff0c;分布式系统成为了构建高可用、高性能应用的基石。然而&#xff0c;随着系统规模的扩张&#xff0c;数据的一致性问题如同幽灵般萦…

Linux字符设备驱动(一) - 框架

字符设备是Linux三大设备之一(另外两种是块设备&#xff0c;网络设备)&#xff0c;字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备&#xff0c;常见的字符设备包括鼠标、键盘、显示器、串口等等&#xff0c;当我们执行ls -l /dev的时候&#xff0c;就能看到大量…

C++容器之vector类

目录 1.vector的介绍及使用1.1vector的介绍1.2vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 空间增长问题1.2.4 vector 增删查改1.2.5vector 迭代器失效问题1.2.6 vector 在OJ中的使用。 2.vector深度剖析及模拟实现2.1 std::vector的核心框架接口…

Kotlin基础知识总结(三万字超详细)

1、条件语句 &#xff08;1&#xff09;if条件 if条件表达式&#xff0c;每一个分支最后一条语句就是该分支的返回值。适用于每个分支返回值类型一致这种情况。 fun getDegree(score: Int): String{val result: String if(score 100){"非常优秀"}else if(score …