【STL专题】深入探索C++之std::string:不止于字符串【万字详解】

欢迎来到CILMY23的博客

🏆本篇主题为:深入探索C++之std::string:不止于字符串

🏆个人主页:CILMY23-CSDN博客

🏆系列专栏:Python | C++ | C语言 | 数据结构与算法 | 贪心算法 | Linux

🏆感谢观看,支持的可以给个一键三连,点赞关注+收藏。


✨写在前头: 

了解完模板和STL后,我们要开始研究std命名空间中的string,作为我们STL学习的开端,string对我们重新认识C语言中的字符串有很大意义。在上文中我们说过容器(Containers),容器是用来存储数据的对象,如数组、链表、树结构等。STL提供了多种类型的容器,本期我们将了解string类,虽然string并没有被归为容器,但它也和容器类似。


string 

一、string的前瞻

1.1 C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问

 我写C语言字符串系列篇的时候还是深有体会的,稍不留神就越界访问导致出错,有时候还难排查出来。

1.2 string的基本概念

string 类是 C++ 标准库中的一部分,它提供了对字符序列的封装,使得字符序列的操作变得既简单又安全。

☄️☄️文档阅读 

我们来看看文档是怎么说的:文档的阅读网站我放在了文章末尾,在这里我分了两部分的文档基本阅读

🍀🍀图一: 

 🍀🍀图二:

☄️☄️信息翻译
  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。 
☄️☄️总结

1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
4. 不能操作多字节或者变长字符的序列。

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

二、string类的常用接口

 💫String 类的默认成员函数

string的默认成员函数我们在这里主要看以下三部分: 

🍃构造函数(⭐)

 进入构造函数的文档界面,我们看到有七个构造函数,其中重点掌握的,也是最常用的,我在图片中已经标出来了。剩下的了解就差不多了。

在这里我们要了解第三个,前面两个很容易理解,但是第三个是什么呢?npos又是什么呢?

我们来看声明和定义,文档在底下给了我们的定义 

string (const string& str, size_t pos, size_t len = npos) 

 解析我在图片中标出来了,大家可以研究研究。

 substring constructor是指字串构造。

现在主要的问题是 npos 是个什么东西? 文档给我们提供了链接,我们可以点击查看(链接)

 

这里一看我们就知道了,原来 npos 是 size_t (无符号整数)的最大值,这就涉及原码反码和补码了(不懂的小伙伴可以看看链接)。 

-1 的补码是32个1,也就是2的32次方-1,但是string构造函数并不会开到这么多空间,所以就只会判定拷贝直到str的末尾。

🍭🍭实际操作 

写了这么多,我们来实际操作一下。在vs 2019上对这几个构造函数进行使用:记得包含头文件<string>

string s0;
string s1("CILMY23");
string s2(s1,2,3);
string s3(s2);//拷贝构造
string s4(s1, 3, string::npos);//从第三个位置开始,拷贝到str字符串末尾
string s5(10, '*');
string s6("CILMY23", 2);

结果如下: 

 

🍃析构函数

对析构函数我们不必关注太多,稍微看看就行

在学习的时候,我们知道析构函数的功能即可 

 🍃赋值运算符重载

在这里赋值运算符重载主要给了三种方式 

 这三种方式的实操如下:

🍭🍭实际操作 

string s7;

s7 = s2; //将s2拷贝给s7
s7 = "CILNY23";//将常量字符串赋值给s7
s7 = '*';//将单字符*赋值给s7

 结果如下:

 赋值运算符重载用的最多的还是前两种,一般第三种还是挺少用的。

 💫String 类的容量操作

string 的容量操作一共有以下操作:

 我们重点学习:size,empty,clear,reserve,resize

 🎈size(⭐),length,capacity,max_size

在string类中,sizelength是一样的,都是返回字符串的长度。,它们表示的是字符串中,有效字符的个数,而capacity表示的是容量,max_size,它返回 string 类型对象最多包含的字符数。也就是string类型支持的最大字符数,超过这个数,将无法支持,编译器会拋出 length_error 异常。

 我们知道在字符串的末尾是有\0的,在这里是不计入size和length的。

为什么这里会相同呢?

 这就不得不提及STL和String的先后问题了,其实是String比STL先出现的,所以String在刚刚的网站中我们是在Container中是找不到String的,但是它跟容器差不多,所以就增加了一个size接口,起初最先我们使用的是lengthsize()length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。 

说完了sizelength,我们接下来看看capacity

 

capacity返回的是开辟空间的大小,它是一个const成员函数,this指针指向的内容是不可以改变的 ,它和size是不一样的,有可能等于size,也可能比size还多

我们可以看一下在vs上它是如何扩容的, 

size_t sz = s1.capacity();
int n;
for (n = 0; n < 1000; n++)
{
	s1.push_back('*');

	if (sz != s1.capacity())
	{
		sz = s1.capacity();
		cout << "capacity changed->" << s1.capacity() << endl;
	}
		
}

结果如下:

我们可以看到它的扩容机制大概是按1.5倍来扩容的 

而g++底下的扩容机制是按照两倍扩容的。 

所以说具体的这些细节,是不确定的。 

 🍭🍭实际操作 

string s1("hello CILMY23");

cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;
cout << s1.max_size() << endl;
cout << s1.npos << endl;

 结果如下:

但是我这里max_size可能是受到了系统或者编译器的限制,在我查阅很多文档后,都说它比npos还小1个,这里得考虑字符'\0'。所以最大长度是不确定的,一般给的是当前平台最大值。

 🎈empty

 文档介绍:这是一个负责测试string是否为空表,如果是空就返回true,否则返回false

这个函数较为简单就不进行实操演示了 

 🎈clear(⭐)

clear函数主要是删除这个string中包含的字符,让它变成一个空表,我们知道空表的判定,是长度length为0,所以这里的clear是不会动capacity容量的,是只会清除有效字符。

 clear() 被调用,string 对象仍然存在,它的内存没有被释放,但其内容被清空了。

 🎈reserve(⭐)

它的作用是请求改变容器的容量。具体来说,它会预分配足够多的内存空间以便存储至少指定数量的元素,这个数量由函数的参数指定。这意味着使用 reserve 可以减少由于添加新元素导致的多次内存重新分配操作,从而提高程序性能。

需要注意的是,reserve 函数只改变容器的容量(即可存储的元素数量,而不必进行再分配),但不改变容器的大小(即实际存储的元素数量)。也就是说,即使调用了 reserve,size() 和 length() 函数的返回值也不会因此改变;reserve 也不会影响容器中已有元素的内容。

  🍭🍭实际操作 

void Teststring2()
{
	string s;
	// 测试reserve是否会改变string中有效元素个数
	s.reserve(100);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
	s.reserve(50);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
}

// 利用reserve提高插入数据的效率,避免增容带来的开销

 结果如下:

但是你开100的空间,编译器不一定会只给你一百个空间,在g++当中,确实是开了一百空间,如果看会不会缩容,就是缩小空间的话,vs是不会缩小的。也就是reserve是比capacity大才会扩容

 🎈resize(⭐)

reserve会比resize用的更加广泛些,让我们来看看resize吧

resize重载了两个函数: 

resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
符个数增多时;resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
元素空间。

注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。 

 🍭🍭实际操作 

void Teststring1()
{
	string s("hello, world");
	cout << s.size() << endl;
	cout << s.length() << endl;
	cout << s.capacity() << endl;

	// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
	// “aaaaaaaaaa”
	s.resize(10, 'a');
	cout << s.size() << endl;
	cout << s.capacity() << endl;

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

	// 将s中有效字符个数缩小到5个
	s.resize(5);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;
}

 结果如下:

 🎈shrink_to_fit(⭐) 

它说的是减少字符串所占用的空间以匹配它的实际内容大小会请求字符串对象减少其容量,以适应它的实际大小,释放未使用的内存。

 🍭🍭实际操作  

假设你有一个字符串 s,如果你在对它进行了多次修改之后,希望确保它不占用额外的空间,你可以调用shrink_to_fit

std::string s = "aaaaaa";

// ... 对字符串s进行了一些操作,可能使其大小发生了变化

s.shrink_to_fit(); // 释放未使用的内存

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

类对象的访问和遍历操作主要有以下两个部分,重点部分是operator[],和迭代器(也就是begin,end)。

⛵⛵类对象的访问
🌠opertor[](⭐) 

运算符重载后的[],支持了两个版本,它的作用是返回pos所在字符串中指向的位置,如果pos和字符串的长度相等,那它就会返回一个‘\0’。

这两个版本主要的差别不是很大,前一个的权限是可读可写,后一个的权限是只读,它们构成了函数重载,是因为this指针被const修饰了。

  🍭🍭实际操作  

我们进入实际操作更能理解重载后的[], 它采用的是 【下标】的形式

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

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

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

 我们使用它就像使用数组一样方便快捷,并且可以特定位置返回修改对应位置的字符。

结果:

🌠at

 在平常使用中,我们更经常使用 [] ,但 at 的功能和它是类似的,同样都是重载了两个对应的函数,也是返回对应下标的字符,区别不同的点在于,at在访问字符串时会执行范围检查。如果尝试访问的位置超出了字符串的当前长度,它会抛出一个异常,从而避免了潜在的未定义行为。

   🍭🍭实际操作  

at的实际操作如下所示: 采用 at(下标) 的形式

//at 和 [] 的区别
void test4()
{
	string str = "hello";

	try {
		// 安全的访问方法,但可能抛出异常
		char ch1 = str.at(10);
	}
	catch (const std::out_of_range& e) {
		// 处理越界访问
		cout << "越界访问异常: " << e.what() << '\n';
	}

	// 不安全的访问方法,但不会抛出异常,在越界时行为未定义
	char ch2 = str[10];  
}
🌠back和front

back和front看起来是C++11才有的,目前大部分编译器都支持这个版本,它实际上是为了规范才提供的,要跟其他容器保持一致,back和front的返回无非就是字符串的最后一个字符和第一个字符。在这里就不做展开讲解了,看看文档就好啦。

⛵⛵string类的遍历

 string类的遍历支持三种方式,一种是迭代器,一种是重载后的[],一种是范围for,但范围for的本质还是迭代器实现的,而且迭代器的遍历是主流的一种方式,更经常使用迭代器,此外我们还会涉及到逆置输出字符串,那就让我们进入实操看看吧。

 🍭🍭实际操作  

 🌠[](⭐) 

 假设我们有个字符串s,采用 size() + [] 的方式进行遍历字符串s,但是注意,这个可不同于C语言中的 [] ,那样是等价于 *()

string s("hello CILMY23");
//[]的遍历
for (int i = 0; i < s.size(); i++)
{
	cout << s[i] << " ";
	cout << s.operator[](i) << " ";
}
cout << endl;

//修改
for (int i = 0; i < s.size(); i++)
{
	s[i] += 1;
	cout << s[i] << " ";
}

cout << endl;

//如果你不想修改,那就用const版本

const string s2("hello CILMY23");
for (int i = 0; i < s.size(); i++)
{
	//无法修改
	s2[i] += 1;

	cout << s2[i] << " ";
}

cout << endl;

}
 🌠迭代器(⭐) 

 迭代器它的行为有点类似指针

string str("hello CILMY23");
//第二个遍历方式,迭代器+begin()+end()
string::iterator it = str.begin();

while (it != str.end())
{
	cout << (*it) << " ";
	++it;
}

cout << endl;

 在这里我们就要补充迭代器的begin位置和end位置了,begin是h,end是‘\0’

同样它也能修改字符内容 

//修改字符
string::iterator it1 = str.begin();
while (it1 != str.end())
{
	*it1 -= 1;
	cout << (*it1) << " ";
	++it1;
}

cout << endl;

 从使用的角度上,[]确实是方便,但是更通用的还是迭代器,迭代器是容器的核心访问,它能访问更多的情况。

 🌠 范围for

 它的本质其实也是一个迭代器

string str1("hello CILMY23");

for (auto e : str1)
{
	cout << e << " ";
}

cout << endl;

 推荐在正向遍历的时候使用,反正也是简洁使用。

💫string类对象的修改操作 

类对象的修改操作一共有以下这么多:

1️⃣ push_back

push_back  的功能是在尾部插入一个字符。但是要注意的是,它在设计上,是真的只能插入一个字符

我们来看实际操作:

string s("hello");

s.push_back('C');
s.push_back('I');
s.push_back('L');
s.push_back('M');
s.push_back('Y');

cout << s << endl;
 2️⃣ append

我们可以看到append一共重载了六个, append 是 string 的一个成员函数,用于向字符串的末尾添加内容。

我们来看实际操作下的append

void test7()
{
    // 创建一个string对象s,并初始化为"hello"
    string s("hello");

    // 向s追加字符串"CILMY23",s变为"helloCILMY23"
    s.append("CILMY23");
    cout << s << endl; // 输出:helloCILMY23

    // 向s追加了10个'*'字符,s变为"helloCILMY23**********"
    s.append(10,'*');
    cout << s << endl; // 输出:helloCILMY23**********

    // 创建另一个空的string对象s1
    string s1;
    // 向s1追加字符串"xx hello CILMY23 xx",s1变为"xx hello CILMY23 xx"
    s1.append("xx hello CILMY23 xx");

    // std::string的begin和end函数返回指向字符串第一个字符和尾后字符的迭代器,respectively
    // 这里,通过迭代器向s追加s1的部分内容:"x hello CILMY23 x"
    // ++s1.begin() 移动到s1的第二个字符,--s1.end()移动到s1的最后一个有效字符
    s.append(++s1.begin(), --s1.end());
    // 输出s此时的内容:helloCILMY23**********x hello CILMY23 x
    cout << s << endl;
    // 注意:第三次追加实际上省略了s1字符串的首尾两个'x'
}

 当然我们在实际操作中,更喜欢用 重载后的+=

3️⃣ operator+=(⭐)

根据以下的文档说明,我们可以直接在字符串末尾添加一个字符串,或者单个字符,又或者是另一个字符对象。 

  🍭🍭实际操作  

void test8()
{
	string s("hello");
	string s1("xxxxxx");
	s += ' ';
	s += "CILMY23";
	s += s1;

	cout << s << endl;
}

 +=使用起来简单方便,清楚明白,在使用中,我个人还是更偏向使用+=的。

4️⃣ insert

insert这个函数,重载形式也蛮多的,在C++中,std::string 类同样提供了 insert 函数来在字符串中的指定位置插入内容。与 append 相比,insert 提供了更高的灵活性,它允许你在字符串的任何位置添加字符或字符串。

   ℹ️ℹ️具体介绍

insert(size_type pos, const std::string& str):
//在当前字符串的 pos 位置插入字符串 str。

insert(size_type pos, const std::string& str, size_type subpos, size_type sublen):
//将字符串 str 从索引 subpos 开始的 sublen 长度的子字符串插入到当前字符串的 pos 位置。

insert(size_type pos, const char* s):
//将字符串 s 插入到当前字符串的 pos 位置。

insert(size_type pos, const char* s, size_type n):
//将字符串 s 的前 n 个字符插入到当前字符串的 pos 位置。

insert(size_type pos, size_type n, char c):
//在当前字符串的 pos 位置插入 n 次字符 c。

insert(iterator p, char c):
//在迭代器 p 指定的位置插入一个字符 c。

insert(iterator p, size_type n, char c):
//在迭代器 p 指定的位置插入 n 次字符 c。

insert(iterator p, InputIterator first, InputIterator last):
//在迭代器 p 指定的位置插入另一个字符串或字符数组,该字符串由 first 到 last 指定的范围决定。

 这个主要作为了解即可,不需要过于在意去记忆。insert最经常用的还是insert(size_type pos, const char* s),切记不要多用,因为它的底层是挪动数据,这样会造成代码效率低下。

5️⃣ assign

 assign:v. 分派,布置(工作、任务);分配(某物);指派,派遣;确定(价值、功能、时间、地点);转让(财产、权利)

assign的意思如上,在C++标准库中,std::string 的 assign 函数用于给字符串赋新值。这个函数替换字符串的当前内容,可以从多种不同类型的源赋值,例如从另一个 string、从字符串,或是直接从字符数组来赋值。

    ℹ️ℹ️具体介绍

assign(const std::string& str):
//用字符串 str 的内容替换当前字符串的内容。

assign(const std::string& str, size_type subpos, size_type sublen):
//用 str 的一个子串替换当前字符串的内容,这个子串从 subpos 开始,长度为 sublen。

assign(const char* s):
//用字符串 s 的内容替换当前字符串的内容。

assign(const char* s, size_type n):
//用字符串 s 的前 n 个字符替换当前字符串的内容。

assign(size_type n, char c):
//用 n 个重复的字符 c 替换当前字符串的内容。

assign(InputIterator first, InputIterator last):
//用两个迭代器 first 和 last 指定范围内的字符替换当前字符串的内容。

 大概就介绍到这里,erase 用的还是比较多的。

6️⃣ erase (⭐)

 erase还是挺好用的,erase 函数用来删除字符串中的一部分内容,是一个非常有用的成员函数,允许多种不同的用法以适应不同的需求。

 ℹ️ℹ️具体操作

string str = "Hello, World!";

// 用法1: 删除从位置3开始的5个字符
str.erase(3, 5);
cout << str << '\n'; // 输出: Hel, World!

// 重置字符串
str = "Hello, World!";

// 用法2: 删除位置为0的字符
auto it = str.erase(str.begin());
cout << str << '\n'; // 输出: ello, World!
cout << *it << '\n'; // 输出e,即下一个字符

// 重置字符串
str = "Hello, World!";

// 用法3: 删除第3个字符到第6个字符之间的所有字符,包括第3个,不包括第6个
str.erase(str.begin() + 2, str.begin() + 6);
cout << str << '\n';  // 输出: He World!

当然它的底层也是通过挪动数据实现的,能不用尽量不用。

7️⃣replace

 在C++中,string 的 replace 成员函数允许我们替换字符串中的某部分内容。这个函数非常灵活,提供了多种重载版本。

  ℹ️ℹ️具体操作

string str = "I like C++ programming.";

// 示例 1: 使用另一个字符串替换
str.replace(7, 3, "Python");
cout << str << '\n';  // 输出: I like Python programming.

// 示例 2: 使用迭代器范围和字符串替换
str.replace(str.begin(), str.begin() + 6, "He loves");
cout << str << '\n';  // 输出: He loves Python programming.

// 示例 3: 使用C风格字符串替换部分内容
str.replace(8, 6, "Java", 4);
cout << str << '\n';  // 输出: He loves Java programming.

// 示例 4: 用字符替换一部分内容
str.replace(str.begin(), str.begin() + 8, 4, 'X');
cout << str << '\n';  // 输出: XXXX Java programming.

 这里就不多做演示了,感兴趣的读者可以自己实验一下

💫string类对象的字符串操作  

💧find(⭐)

 如果我们想覆盖字符串,删除,替换,需要找到一个位置的时候,find()就显得极其重要。find 函数用于在字符串内查找子串或字符的第一个出现位置如果找到了匹配项,它就返回匹配项的下标;如果没有找到,它则返回 npos,这是一个特别定义的常量,表示不存在的位置。

find(const std::string& str, size_type pos = 0) const:
从位置 pos 开始搜索字符串 str。
find(const char* s, size_type pos = 0) const:
从位置 pos 开始搜索以空字符结尾的字符串 s。
find(const char* s, size_type pos, size_type n) const:
从位置 pos 开始,搜索字符串 s 的前 n 个字符。
find(char c, size_type pos = 0) const:
从位置 pos 开始,搜索字符 c。

   ℹ️ℹ️具体操作

string str = "We are looking for the term in this string.";

// 查找子串
size_t found = str.find("term");
if (found != std::string::npos)
	cout << "Found 'term' at index: " << found << '\n';

// 查找C风格的字符串
found = str.find("the term");
if (found != std::string::npos)
	cout << "Found 'the term' at index: " << found << '\n';

// 查找单个字符
found = str.find('t');
if (found != std::string::npos)
	cout << "Found 't' at index: " << found << '\n';

// 从某一位置后开始查找
found = str.find('i', 10);
if (found != std::string::npos)
	cout << "Found 'i' after index 10 at index: " << found << '\n';

// 查找不存在的字符串
found = str.find("nonexistent");
if (found == std::string::npos)
	cout << "Did not find 'nonexistent'.\n";
💧rfind 

在C++中,string 类提供了 rfind 函数用于从字符串的末尾开始向前搜索子串或者字符的最后一次出现的位置。如果找到了匹配项,它返回该匹配项的起始下标,如果没有找到,它返回npos。

rfind(const std::string& str, size_type pos = npos) const:
从位置 pos 开始向前搜索字符串 str 的最后一次出现。
rfind(const char* s, size_type pos = npos) const:
从位置 pos 开始向前搜索字符串 s 的最后一次出现。
rfind(const char* s, size_type pos, size_type n) const:
从位置 pos 开始向前搜索字符串 s 的前 n 个字符的最后一次出现。
rfind(char c, size_type pos = npos) const:
从位置 pos 开始(默认为字符串的末尾)向前搜索字符 c 的最后一次出现。

ℹ️ℹ️具体操作 

string str = "The rain in Spain falls mainly in the plain.";

// 查找子串的最后一次出现
size_t found = str.rfind("in");
if (found != std::string::npos)
	cout << "Last occurrence of 'in' found at index: " << found << '\n';

// 查找字符的最后一次出现
found = str.rfind('a');
if (found != std::string::npos)
	cout << "Last occurrence of 'a' found at index: " << found << '\n';

// 查找子串的最后一次出现,但不超过索引 10
found = str.rfind("in", 10);
if (found != std::string::npos)
	cout << "Last occurrence of 'in' before index 10 found at index: " << found << '\n';

// 查找不存在的字符串
found = str.rfind("xyz");
if (found == std::string::npos)
	cout << "'xyz' not found.\n";

 rfind 用于找到特定的字符或者子串在字符串中最后一次出现的位置。如果需要从字符串的结尾向前查找,rfind 是个理想的选择。如果想要找字符串中某个子串或字符的第一个出现,应当使用 find 函数。

💧c_str

c_str() 返回一个指向正规C字符串的指针,常量,即以空字符结束的字符数组。这个方法用于获取一个C风格的字符串版本,通常是为了与需要传统C字符串的C语言API兼容。

这是一个非常实用的功能,因为它允许 string 对象以C风格的字符串形式与那些早期设计的或者以纯C编写的接口(APIs)交互。例如,一些系统调用和库函数要求传入一个以 NULL 终止的字符数组(char* 或 const char*),这时你可以使用 c_str() 函数。

 

ℹ️ℹ️具体操作

string str = "Example string";
    
 // 使用c_str()获取C风格的字符串
 const char* cstyle_str = str.c_str();
    
 // 输出C风格的字符串
 cout << cstyle_str << endl;

在上面的例子中,c_str() 返回了一个指向以 NULL 结尾的字符串 'Example string\0' 的指针。这个指针可以被传递给任何需要C风格字符串的函数

需要注意的一点是,由 c_str() 返回的字符数组的生存期与它所属的 string 对象相同,并且如果在 c_str() 调用后对原字符串对象进行了修改(除了添加字符到字符串的末尾以外),返回的字符指针可能就不再有效。因此,最好是在需要使用C风格字符串的时候才调用 c_str(),并且避免在之后修改字符串。

 

💧substr (⭐)

💫string类对象的非成员函数重载   

三、string类的接口一览图 

 接口一览:从文档中整理了一份全部接口的思维导图


文档网站:
cplusplus.com/reference/string/string/?kw=stringicon-default.png?t=N7T8https://cplusplus.com/reference/string/string/?kw=string

字符编码:

【Python】python编程初探2---字符编码,输入和输出,初识数据类型-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/sobercq/article/details/137061735


文档学习小结:

sequences :n. 序列,顺序;继起的事,是sequence的复数形式

features : n.产品特点,特征;容貌;嘴脸(feature 的复数)

specifically : adv. 特意,专门地;明确地,具体地;具体来说,确切地说;局限性地;专门;

instantiation : n. 实例化

handle :  v. 拿;处理,应付;操纵;触(球);经营,管理

default : adj. 默认的

variable :adj. 易变的,多变的;时好时坏的;可变的,可调节的;(数)(数字)变量的;(植,动)变异的,变型的;(齿轮)变速的

assignment :  n. 作业,任务;(工作等的)分配,指派;(财产、权利的)转让,在默认成员函数中,意指赋值。

substring :n. 子串;子链

constructor :n. 构造函数;构造器;建造者

portion :n. (某物的)一部分;(尤指餐馆中食物的)一份,一客;(责任、过失、职责等的)一份,一部分;<法律>(根据法律赠与或遗留给继承人的)一份财产;<古> 命运,天数

spans : v. 跨越;持续;贯穿(span 的第三人称单数)

consecutive : adj. 连续的,不间断的

 assign:v. 分派,布置(工作、任务);分配(某物);指派,派遣;确定(价值、功能、时间、地点);转让(财产、权利)


🛎️感谢各位同伴的支持,本期C++就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞,关注+收藏,若有不足,欢迎各位在评论区讨论。    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx - 一键实现Nginx的快速安装和优化配置

文章目录 思路实现二次优化 思路 初始化下载工具目录并下载依赖&#xff1a; 创建临时目录 /tmp/tools。下载 OpenSSL、PCRE 和 Zlib 的压缩包。解压这些依赖包到指定目录。 设置NGINX的用户和脚本&#xff1a; 添加 nginx 用户。创建目录和启动、停止、重载NGINX的脚本。 安装…

如何异地组网添加摄像机?

本文将介绍如何使用天联技术实现异地组网添加摄像机&#xff0c;并保障数据的安全性。 安防摄像机的应用愈发广泛&#xff0c;无论是家庭安防还是企业监控&#xff0c;摄像机都扮演着重要角色。在一些特殊场合或者特殊需求下&#xff0c;我们需要将摄像机添加到异地网络中进行监…

Web开发——HTMLCSS

1、概述 Web开发分前端开发和后端开发&#xff0c;前端开发负责展示数据&#xff0c;后端开发负责处理数据。 HTML&CSS是浏览器数据展示相关的内容。 1&#xff09;网页的组成部分 文字、图片、音频、视频、超链接、表格等等 2&#xff09;网页背后的本质 程序员写的前端…

神经网络基础结构

1. 神经网络 在神经网络中&#xff0c;每个神经元都有一个与之关联的权重和偏置&#xff0c;它们用于计算神经元的输出值。神经元接收来自上一层神经元的输入&#xff0c;并将这些输入与权重相乘并加上偏置&#xff0c;然后通过激活函数进行非线性处理&#xff0c;最终产生输出…

Qt案例练习(有源码)

项目源码和资源&#xff1a;Qt案例练习: qt各种小案例练习,有完整资源和完整代码 1.案例1 项目需求&#xff1a;中间为文本框&#xff0c;当点击上面的复选框和单选按钮时&#xff0c;文本框内的文本会进行相应的变化。 代码如下&#xff1a; #include "dialog.h" …

【全开源】智能名片系统源码(Fastadmin+ThinkPHP和Uniapp)

数字时代的新名片&#xff0c;连接未来的桥梁 引言 在数字化浪潮的推动下&#xff0c;传统名片已经逐渐淡出人们的视线。取而代之的是智能名片系统&#xff0c;它以其高效、便捷和智能化的特点&#xff0c;成为了商务交流的新宠。而智能名片系统源码&#xff0c;作为其核心驱…

nextcloud 安装部署

php版本不对 ubuntu nginx 配置php 网站-CSDN博客 抄自chatgpt ubuntu完全卸载干净某个包-CSDN博客 以及设置基本的php nginx环境参照上面两篇博文 然后参照官方文档 Example installation on Ubuntu 22.04 LTS — Nextcloud latest Administration Manual latest document…

datasheet芯片数据手册—新手入门学习(二)【8-18】

参考芯片手册已经上传&#xff0c;可自行下载 因为芯片参考手册内容比较多&#xff0c;故再一次介绍本文内容主要讲解章节。 目录 8、内容介绍 命令真值表 9、Command Definitions 10、READ Operations &#xff08;1&#xff09;页面读取操作 &#xff08;2&#xff…

Docker 开启 SSL 验证

最近看 OJ 项目的远程开发阶段&#xff0c;然后踩坑踩了 2 天&#x1f602; Docker 版本&#xff1a;在 CentOS 安装 sudo yum install docker-ce-20.10.9 docker-ce-cli-20.10.9 containerd.io Client: Docker Engine - CommunityVersion: 20.10.9API version: …

1673. 找出最具竞争力的子序列

题目 给定一个整数数组 nums 和一个正整数 k&#xff0c;返回长度为 k 且最具竞争力的 nums 子序列。 数组的子序列是从数组中删除一些元素&#xff08;可能不删除元素&#xff09;得到的序列。 在子序列 a 和子序列 b 第一个不相同的位置上&#xff0c;如果 a 中的数字小于…

Redis系统架构中各个处理模块是干什么的?no.19

Redis 系统架构 通过前面的学习&#xff0c;相信你已经掌握了 Redis 的原理、数据类型及访问协议等内容。本课时&#xff0c;我将进一步分析 Redis 的系统架构&#xff0c;重点讲解 Redis 系统架构的事件处理机制、数据管理、功能扩展、系统扩展等内容。 事件处理机制 Redis…

[论文精读]Variational Bayesian Last Layers

论文网址&#xff1a;Variational Bayesian Last Layers (arxiv.org) 论文代码&#xff1a;GitHub - VectorInstitute/vbll: Simple (and cheap!) neural network uncertainty estimation 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以…

leetcode437 路径总和III-哈希表+前缀和

题目 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到子节…

服务器数据恢复—EVA存储多块硬盘离线导致部分LUN丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 1台某品牌EVA4400控制器3台EVA4400扩展柜28块FC硬盘。 服务器故障&#xff1a; 由于两块磁盘掉线导致存储中某些LUN不可用&#xff0c;某些LUN丢失&#xff0c;导致存储崩溃。 服务器数据恢复过程&#xff1a; 1、由于EVA4400存储故障是某些磁…

Web API——获取DOM元素

目录 1、根据选择器来获取DOM元素 2.、根据选择器来获取DOM元素伪数组 3、根据id获取一个元素 4、通过标签类型名获取所有该标签的元素 5、通过类名获取元素 目标&#xff1a;能查找/获取DOM对象 1、根据选择器来获取DOM元素 语法&#xff1a; document.querySelector(css选择…

python从0开始学习(十二)

目录 前言 1、字符串的常用操作 2、字符串的格式化 2.1 格式化字符串的详细格式&#xff08;针对format形式&#xff09; ​编辑 总结 前言 上一篇文章我们讲解了两道关于组合数据类型的题目&#xff0c;本篇文章我们将学习新的章节&#xff0c;学习字符串及正则表达式。 …

C++|红黑树(分析+模拟实现插入)

目录 一、概念 二、红黑树插入的实现 2.1红黑树节点的定义 2.2红黑树基础架构 2.3红黑树的插入 2.3.1按照二叉搜索树的规则插入新结点 2.3.2检测新插入节点&#xff0c;是否破坏红黑树性质来进行调整 2.3.2.1cur为红&#xff0c;p为红&#xff0c;g为黑&#xff0c;u存…

好用的桌面备忘录是哪个?备忘录软件哪个更好用?

备忘录软件已成为我们日常生活和工作中不可或缺的工具&#xff0c;它能帮助我们记录重要事项、安排日程&#xff0c;从而提高工作效率&#xff0c;减少遗忘。在繁忙的工作和生活中&#xff0c;一款好用的备忘录软件往往能让我们事半功倍。 在众多的备忘录软件中&#xff0c;敬…

Jenkins 构建 Web 项目:构建服务器和部署服务器分离的情况

构建命令 #!/bin/bash node -v pnpm -v pnpm install pnpm build:prod # 将dist打包成dist.zip zip -r dist.zip dist

2024年艺术鉴赏与文化传播国际会议(AACC 2024)

2024年艺术鉴赏与文化传播国际会议&#xff08;AACC 2024&#xff09; 2024 International Conference on Art Appreciation and Cultural Communication 【重要信息】 大会地点&#xff1a;贵阳 大会官网&#xff1a;http://www.icaacc.com 投稿邮箱&#xff1a;icaaccsub-co…