C++中的string类

本文详细介绍了C++中string类的构造方法、常用接口(包括容量操作、访问遍历、修改操作),以及与之相关的非成员函数。通过实例演示,帮助读者理解如何在编程中有效地使用和管理字符串对象。
摘要由CSDN通过智能技术生成

一、概述

在编程过程中,我们常常与字符串打交道,从我们写的第一个"hello world!",到后面提升难度写查询字符串中是否存在子串,这些过程中均包含着对字符串的处理,在C中利用#include "string.h"就可以利用这个库里的许多模板,如strlen、strcpy....总之有很多函数供大家使用。相应的在C++中标准模板库也(STL)也实现了string类来供大家使用。下面就来详细介绍C++中的string类。

C++是面向对象的高级语言,所以其中的这里实现的string也只是它里面的一个类。既然是类,那么我们就要知道,我们是使用的是他的接口。

二、常用接口

1、构造字符串

函数功能
string
构造空的 string 类对象,即空字符串
string(const char* s)
C-string 来构造 string 类对象
string(size_t n,char)
string 类对象中包含 n 个字符 c
string(const string&s)
拷贝构造函数

使用场景如下,这下就可以理解了。这是string类对象的常见构造

注意使用string类时,必须包含#include头文件以及using namespace std;

void Teststring()
{
	string s1; // 构造空的string类对象s1
	string s2("hello world!"); // 用C格式字符串构造string类对象s2
	string s3(10, 'x'); // 创建一个由10个'x'字符组成的字符串 "xxxxxxxxxx"
	string s4(s2); // 拷贝构造s4
}

2、string类对象的容量操作

其实这一部分就是string类里实现好的成员函数。我们调用的时候也很简单,就是 对象.方法 就行了

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

下面我们用代码来理解这些函数(分析均写在函数注释里、请耐心看完)

void Teststring1()
{
	string s("hello, bit!!!");
	cout << s.size() << endl;//结果是13 
	cout << s.length() << endl;//结果是13 
//size()与length()方法底层实现原理完全相同,
//引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size
//所以也不难理解为什么C++这两个函数为什么作用一样还都存在了。

	cout << s.capacity() << endl;//结果是15
//其实capacity()返回的就是std::string对象当前分配的存储空间的大小,
//这个大小至少足够存储当前字符串,可能还有额外的空间以减少将来添加更多字符时的
//重新分配次数。一个std::string对象的容量通常是实现定义的,这意味着它依赖于
//你的C++标准库的实现和可能的内存分配策略。一些实现可能初始分配比实际字符串长度更多的
//空间,以优化追加操作;而其他实现可能正好分配等同于字符串长度的空间。
//从英文单词上也可以理解它的意思。capacity 名词 容积,容纳能力。

	cout << s << endl;
// 注意:string类对象支持直接用cin和cout进行输入和输出

	// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s.clear();
	cout << s.size() << endl; //结果是0
	cout << s << endl; //结果是空
	cout << s.capacity() << endl; //还是15

	// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
	// “aaaaaaaaaa”
	s.resize(10, 'a');
	cout << s.size() << endl;//结果是10
	cout << s.capacity() << endl;//结果是15
	// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
	// "aaaaaaaaaa\0\0\0\0\0"
	// 注意此时s中有效字符个数已经增加到15个
	s.resize(15);
	cout << s.size() << endl;//结果是15
	cout << s.capacity() << endl;//结果是15
	cout << s << endl;
	// 将s中有效字符个数缩小到5个
	s.resize(5);
	cout << s.size() << endl;//结果是5
	cout << s.capacity() << endl;//结果是15
	cout << s << endl;//aaaaa
}

这一段代码后,上面的函数都讲完了,还差个empty和reserve,empty很好理解就是检测字符串释放为空串,是返回true,否则返回false,我们再来分析分析reserve。

reserve的作用是为字符串预留空间。

void Teststring2()
{
	string s;
	// 测试reserve是否会改变string中有效元素个数
	s.reserve(100);
	cout << s.size() << endl;//是0
	cout << s.capacity() << endl;//是111
	// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
	s.reserve(50);
	cout << s.size() << endl;//是0
	cout << s.capacity() << endl;//是111
//根据C++标准,reserve()的实现并不要求减小容量,如果参数小于当前容量。
//换句话说,reserve()保证至少有指定数量的空间,但如果已经有更多的空间,它不会减少这个空间。
}
// 利用reserve提高插入数据的效率,避免突然增容带来的时间开销

看到这里,大家对函数可能都已经理解了,但是对capacity()好像感觉还不是那么到位,我在举两个例子让大家来判断一下。

void TestPushBack()
{
	string s;
	size_t sz = s.capacity();
//在32位系统中,size_t通常被定义为unsigned int,而在64位系统中,
//它通常被定义为unsigned long long或unsigned long,这取决于编译器和操作系统。
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
//push_back是一个成员函数,广泛应用于C++标准库中的容器类
//这个函数的作用是向容器的末尾添加一个新元素。
		if (sz != s.capacity())//如果容量变了就输出出来
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}
//分析一下,这段代码通过不断地向一个std::string对象s添加字符来观察其容量的变化。
//在C++中,std::string(以及其他动态容器,如std::vector)的容量指的是
//它在需要重新分配内存之前可以容纳的元素数量。当你向std::string添加元素时,
//如果当前容量不足以容纳新的元素,其容量将会自动增加。



void TestPushBackReserve()
{
	string s;
	s.reserve(100);
	size_t sz = s.capacity();

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}
//那再看这段代码,手到擒来,Reserve已经预留好空间了,而且还很大,所以一直没有
//当前容量不足以容纳新的元素,故这段代码不会输出什么东西

3、string类对象的访问及遍历操作

这个也是字符串中也是十分常用的操作。

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

光看这些名字肯定是一头雾水,我们上代码欣赏一下它的作用。

这个写在前面大家先理解一下。

void Teststring1()
{
	string s1("hello Bit");
	const string s2("Hello Bit");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;

	// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}

(关于这个const string不清楚的 参见大佬文章关于C/C++中const的用法-CSDN博客 本文不细述)

void Teststring2()
{
	string s("hello Bit");
	// 3种遍历方式:
	// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
	// 另外以下三种方式对于string而言,第一种使用最多
	// 1. for+operator[]
	for (size_t i = 0; i < s.size(); ++i)
		cout << s[i] << endl;

	// 2.迭代器
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << endl;
		++it;
	}

	string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
		cout << *rit << endl;
//在C++中,迭代器是一种访问容器中元素的机制,它提供了类似于指针的接口。
//迭代器封装了对容器元素的访问方式,使得你可以通过迭代器遍历容器中的元素,
//而不需要知道容器的内部表示。
//当你使用*it时,你实际上是在解引用迭代器it。
//解引用迭代器的操作返回对应于迭代器当前位置的元素的引用。
//这就像使用指针一样,其中指针指向某个元素,你需要解引用指针来获取该元素的值。


	// 3.范围for
	for (auto ch : s)
		cout << ch << endl;
}

4、string类对象的修改操作

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

上代码理解。

void Teststring()
{
	string str;
	str.push_back(' '); // 在str后插入空格
	str.append("hello"); // 在str后追加一个字符"hello"
	str += 'b'; // 在str后追加一个字符'b' 
	str += "it"; // 在str后追加一个字符串"it"
	cout << str << endl;
	cout << str.c_str() << endl; // 以C语言的方式打印字符串

	// 获取file的后缀
	string file("string.cpp");
	size_t pos = file.rfind('.');
	string suffix(file.substr(pos, file.size() - pos));
	cout << suffix << endl;

	// npos是string里面的一个静态成员变量
	// static const size_t npos = -1;

	// 取出url中的域名
	string url("http://www.cplusplus.com/reference/string/string/find/");
	cout << url << endl;
	size_t start = url.find("://");
	if (start == string::npos)
	{
		cout << "invalid url" << endl;
		return;
	}
	start += 3;
	size_t finish = url.find('/', start);
	string address = url.substr(start, finish - start);
	cout << address << endl;

	// 删除url的协议前缀
	pos = url.find("://");
	url.erase(0, pos + 3);
	cout << url << endl;
}

结果

5、string类非成员函数

C++的std::string类有一些与之关联的非成员函数,这些函数提供了额外的功能,而不直接属于任何一个std::string对象。以下是一些常用的非成员函数,也比较杂乱无章吧,更多的用法都是等待读者去发现使用。这里仅举例子。

operator+
尽量少用,因为传值返回,导致深拷贝效率低
operator>>
输入运算符重载
operator<<
输出运算符重载
getline
获取一行字符串
relational operators

大小比较

swap

交换两个字符串的内容

三、结语

上面已经对string类进行了简单的介绍,足够大家使用了。如果还想更深入了解,等我再出一篇关于简单实现string类的文章,这个也是面试时常问的问题。有缘再见。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caihua_X

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值