前言
前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个‘对象’,也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点——STL(string类)。下面话不多说坐稳扶好咱们要开车了😍
一、STL简介
1.STL是什么
C++的Standard Template Library(STL)是C++标准库中一个包含了丰富的模板类和函数的部分,它提供了许多常用的数据结构和算法,以及与之相关的功能组件。STL的设计目标是提供通用、高效和可重用的模板类,以便于开发人员编写高质量的C++代码。
2.STL的内容
STL包含六个主要组件:分别是容器(Containers)、算法(Algorithms)、迭代器(Iterators)、函数对象(Function Objects)、适配器(Adapters)和分配器(Allocators),这六大组件相互之间紧密配合,提供了丰富而强大的功能,使得开发人员能够更高效地进行数据管理、算法处理和代码编写。 下面对这些组件进行简单的介绍(后面的文章也会对这些逐一分析)
1.容器(Containers):容器是STL中用于存储数据的组件。STL提供多种容器类,像向量(vector)、链表(list)、队列(queue)、栈(stack)、集合(set)、映射(map)等。每种容器都有特定的特性和适用场景,开发人员可以根据需求选择合适的容器类来管理数据。
2.算法(Algorithms):算法是STL中的核心部分。它提供了丰富的函数模板,用于处理容器中的元素。STL的算法包括排序、搜索、复制、变换等功能,比如 sort、find、copy 等。算法通常通过迭代器来操作容器,因此可以很方便地应用于各种容器,并且具有良好的可重用性和效率。
3.迭代器(Iterators):迭代器是STL中的通用遍历机制。它相当于指针一样,用于遍历容器中的元素。迭代器提供了对容器元素的访问和操作方法,使得算法可以在不依赖具体容器类型的情况下进行工作。STL提供了多种类型的迭代器,包括输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器,根据容器和算法的要求选择合适的迭代器类型。
4.函数对象(Function Objects):函数对象是可以像函数一样调用的对象。STL中的算法和某些容器类使用函数对象来指定操作的方式,如排序顺序、元素匹配等。STL提供了一些已定义的函数对象,比如等于判断(equal_to)、比较大小(less)、取反(not)等,也可以自定义函数对象来适应特定需求。
5.适配器(Adapters):适配器是STL中的一种特殊组件。它可以将一种容器或迭代器的接口转换为另一种接口,以便于在不同上下文中使用。STL提供了多种适配器,如堆栈适配器(stack)、队列适配器(queue)和优先队列适配器(priority_queue),它们在底层使用其他容器类,通过适配器接口提供不同的行为。
6.分配器(Allocators):分配器用于管理容器使用的内存分配和释放。STL提供了默认的分配器(allocator),也允许开发人员定义自己的分配器。分配器可以根据具体的内存需求进行内存管理,以提高效率和灵活性。
3.STL的使用前提
- 了解C++基础知识:STL是建立在C++语言的基础上的,因此使用STL需要对C++基础知识有一定的了解,包括类、继承、模板等概念。
- 熟悉STL的组件:要使用STL,需要对STL的六大组件(容器、算法、迭代器、函数对象、适配器和分配器)有一定的了解,知道它们的特性、用法以及如何选择合适的组件来解决问题。
- 理解模板编程:STL使用了模板编程的技术,在使用STL时需要理解模板的基本语法以及模板的实例化和函数重载等机制。
二、string类
1.string类 是什么
在C++中,std::string是标准库提供的一个类,用于处理字符串。它是基于模板的容器类,位于命名空间std中。std::string类提供了许多成员函数和操作符,用于对字符串进行各种操作,比如插入、删除、查找、连接等。与C风格的字符串相比,std::string类更加安全和方便,它负责自动管理字符串内存,具有动态大小调整的能力。
2.string类的特点和操做
⭕构造和初始化
string类提供了多种构造函数和初始化方式,用于创建和初始化字符串对象。以下是 std::string 类的构造函数和初始化方式的介绍:
✅默认构造函数
string();
创建一个空的字符串对象。
✅字符串常量初始化
string(const char* s); string(const char* s, size_type count);
使用C风格字符串 s 来初始化字符串对象。传入的字符串可以是以 null 字符结尾的字符数组( const char* ),或者可以指定字符个数( size_type count )。
✅复制构造函数
string(const string& other);
使用一个已有的字符串对象 other 来创建一个新的字符串对象。新对象是 other 的副本,内容相同。
✅填充构造函数
string(size_type count, char ch);
创建一个包含 count 个字符 ch 的字符串对象。
✅迭代器范围初始化
template <class InputIt> string(InputIt first, InputIt last);
使用迭代器指定的字符范围 [first, last) 来初始化字符串对象。这使得可以使用其他容器或字符数组等作为初始化源。
✅初始化列表初始化
string(initializer_list<char> ilist);
使用初始化列表来初始化字符串对象。
除了构造函数,还可以使用赋值运算符 = 将一个字符串对象赋值给另一个字符串对象。
std::string str8 = str1; // 使用赋值运算符 str1 = str2; // 使用赋值运算符
通过这些构造函数和初始化方式,可以创建并初始化字符串对象,以便对其进行后续的字符串操作和处理。
✅初始化总结
int main() { std::string str1; // 默认构造函数 std::string str2("Hello"); // 字符串常量初始化 std::string str3("Hello", 3); // 字符串常量初始化,指定字符个数 std::string str4(str2); // 复制构造函数,创建副本 std::string str5(5, 'A'); // 填充构造函数,创建包含5个字符'A'的字符串 std::string str6(str2.begin(), str2.end());// 迭代器范围初始化 std::string str7{'C', 'P', 'P'}; // 初始化列表初始化 std::string str8 = str1; // 使用赋值运算符 str1 = str2; // 使用赋值运算符 return 0; }
⭕字符串大小和容量
在 std::string 类中,可以使用成员函数来获取字符串的大小和容量。
✅字符串大小
size() , length() : 返回字符串中字符的个数(即字符串的长度)。
std::string str = "Hello"; std::cout << str.size(); // 输出:5 std::cout << str.length(); // 输出:5
✅字符串容量
capacity() : 返回字符串内存分配的容量,即字符串可以容纳的字符数,而不会进行重新分配。
reserve(size_type count) : 请求将字符串的容量设置为至少 count 个字符。
shrink_to_fit() : 要求字符串按照实际大小来分配内存,即缩小容量以适应当前字符串的大小。
std::string str = "Hello"; std::cout << str.capacity(); // 输出:15 str.reserve(20); std::cout << str.capacity(); // 输出:20 str.shrink_to_fit(); std::cout << str.capacity(); // 输出:5
🚨注意
- 字符串的大小是指字符的个数,而不是字符串占用的字节数。一个字符可以占用多个字节的存储空间,例如UTF-8编码中的某些特殊字符。
- 字符串的容量不一定等于大小。字符串的容量取决于内存的分配策略和字符串的动态增长过程。通常情况下,当增加字符串长度时,字符串会自动重新分配更大的内存块,以容纳新的字符,从而增加容量。
- 调用reserve()函数可以预留一定的容量,避免频繁的内存重新分配,提高性能。但是并不总是需要手动调用reserve(),因为std::string类会根据实际需要自动调整容量。
通过上面这些函数,可以方便地获取和管理字符串的大小和容量信息。
⭕字符访问和修改
在std::string类中,提供了多种方法来访问和修改字符串中的字符,通过下面这些函数,可以方便地访问和修改字符串中的特定字符,或者在字符串的末尾添加或删除字符。
✅字符访问
- operator[](size_type pos): 使用下标操作符 [] 来访问指定位置 pos 处的字符。
- at(size_type pos): 使用 at() 函数来访问指定位置 pos 处的字符,并进行边界检查。
- front(): 获取字符串的第一个字符。
- back(): 获取字符串的最后一个字符。
std::string str = "Hello"; char ch1 = str[0]; // 使用下标操作符访问首字符,ch1='H' char ch2 = str.at(2); // 使用at()函数访问第三个字符,ch2='l' char ch3 = str.front(); // 获取字符串的第一个字符,ch3='H' char ch4 = str.back(); // 获取字符串的最后一个字符,ch4='o'
✅字符修改
- operator[](size_type pos): 使用下标操作符 [] 来修改指定位置 pos 处的字符。
- at(size_type pos): 使用 at() 函数来修改指定位置 pos 处的字符,并进行边界检查。
- push_back(char ch): 在字符串的末尾添加一个字符。
- pop_back(): 删除字符串的末尾字符。
- clear(): 清空字符串中的所有字符。
- resize(size_type count): 改变字符串的大小。新的大小会影响字符串的字符个数。
- resize(size_type count, char ch): 改变字符串的大小,并用指定字符 ch 填充新增部分。
std::string str = "Hello"; str[0] = 'h'; // 使用下标操作符修改首字符,字符串变为"hello" str.at(3) = 'P'; // 使用at()函数修改第四个字符,字符串变为"helPo" str.push_back('!'); // 在末尾添加字符'!',字符串变为"helPo!" str.pop_back(); // 删除末尾的字符,字符串变为"helPo" str.clear(); // 清空字符串,字符串成为空字符串 str.resize(7); // 改变字符串大小为7,添加空字符,即字符串变为"helPo\0\0" str.resize(10, '*'); // 改变字符串大小为10,用'*'填充剩余部分,即字符串变为"helPo****"
⭕字符串连接和拼接
在std::string类中,提供了多种方法来进行字符串的连接和拼接操作,以合并多个字符串为一个字符串,这些方法可以通过操作符重载和成员函数来实现。
✅字符串连接
operator+ :使用 + 操作符将两个字符串连接起来,产生一个新的字符串。
operator+= :使用 += 操作符将一个字符串追加到另一个字符串的末尾,修改原字符串。
std::string str1 = "Hello"; std::string str2 = "World"; std::string str3 = str1 + str2; // 使用+操作符进行字符串连接,得到字符串"HelloWorld" str1 += str2; // 使用+=操作符将str2追加到str1的末尾,修改str1的值为"HelloWorld"
✅字符串拼接
append(const char* s):将一个以 null 字符结尾的字符数组(C风格字符串)追加到当前字符串的末尾。
append(const std::string& str):将另一个字符串追加到当前字符串的末尾。
append(const char* s, size_type count):将指定个数的字符从一个以 null 字符结尾的字符数组(C风格字符串)追加到当前字符串的末尾。
std::string str = "Hello"; str.append(" World"); // 将C风格字符串追加到字符串末尾,字符串变为"Hello World" std::string otherStr = " from C++"; str.append(otherStr); // 将另一个字符串追加到字符串末尾,字符串变为"Hello World from C++"
以上方法可以灵活地进行字符串的连接和拼接操作,使用不同的方法可以根据具体需求选择最合适的方式来实现字符串的合并。